1. Docs
  2. Developers

REST API

Read and change your TalkingDot workspace from your own servers: sync contacts, record events, start and answer conversations, and manage help-center articles.

Overview

The REST API is organised around resources (contacts, conversations, articles and so on), uses standard HTTP methods and status codes, and accepts and returns JSON. Every request is scoped to the workspace that owns the API key.

Base URLhttps://talkingdot.com/api/v1
AuthenticationAuthorization: Bearer <api key>
FormatJSON request bodies and JSON responses
IDsIntegers
TimestampsISO 8601 in UTC, for example 2026-10-03T09:15:00Z
Rate limit120 requests per minute per API key

Try it with your key:

Shell
curl https://talkingdot.com/api/v1/me \
  -H "Authorization: Bearer cn_live_xxxxxxxx"

Authentication

Authenticate every request with an API key in the Authorization header, as a bearer token. Workspace admins create keys in Settings → API. Keys look like cn_live_xxxxxxxx and belong to one workspace.

Shell
curl https://talkingdot.com/api/v1/contacts \
  -H "Authorization: Bearer cn_live_xxxxxxxx"

A request with a missing, invalid or revoked key gets a 401 response with the error code unauthorized.

Keep API keys secret. A key can read and change your whole workspace, including contacts' personal data. Use it only from your servers: never in browser JavaScript, mobile apps or public repositories. Copy a new key when it's created, because only a hash of it is stored. If a key leaks, revoke it in Settings → API and create a new one.

Requests and responses

Send request bodies as JSON with a Content-Type: application/json header. Every response is JSON.

A successful response has a 2xx status (for example 201 Created for a new contact) and wraps the result in data. For a single object, data is that object:

JSON
{
  "data": {
    "id": 1842,
    "name": "Ada Lovelace",
    "email": "ada@example.com"
  }
}

For lists, data is an array and meta holds the pagination cursor:

JSON
{
  "data": [
    { "id": 1842, "name": "Ada Lovelace", "email": "ada@example.com" }
  ],
  "meta": {
    "next_cursor": "eyJpZCI6MTg0Mn0"
  }
}

These two snippets are shortened; real objects contain every field listed in the object reference. Fields without a value are null, never left out.

Pagination

List endpoints return results a page at a time. Control the page with two query parameters:

ParameterTypeDescription
limitintegerHow many results to return, from 1 to 100. Default 25.
cursorstringThe meta.next_cursor value from the previous page. Leave it out for the first page.

Keep requesting with the latest next_cursor until it comes back as null. Treat cursors as opaque strings: don't build or change them yourself.

Shell
curl "https://talkingdot.com/api/v1/conversations?status=open&limit=100" \
  -H "Authorization: Bearer cn_live_xxxxxxxx"

curl "https://talkingdot.com/api/v1/conversations?status=open&limit=100&cursor=eyJpZCI6NTUzMX0" \
  -H "Authorization: Bearer cn_live_xxxxxxxx"

The same loop in Node.js 18+:

JavaScript
async function listAllOpenConversations() {
  const results = [];
  let cursor = null;

  do {
    const url = new URL('https://talkingdot.com/api/v1/conversations');
    url.searchParams.set('status', 'open');
    url.searchParams.set('limit', '100');
    if (cursor) url.searchParams.set('cursor', cursor);

    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${process.env.TALKINGDOT_API_KEY}` },
    });
    if (!res.ok) throw new Error(`API request failed with HTTP ${res.status}`);

    const body = await res.json();
    results.push(...body.data);
    cursor = body.meta.next_cursor;
  } while (cursor);

  return results;
}

Errors

Failed requests return a 4xx status and an error object. Branch on code; message is a human-readable explanation that may change.

JSON
{
  "error": {
    "code": "not_found",
    "message": "Contact not found."
  }
}
HTTP statuscodeMeaning
401unauthorizedThe API key is missing, invalid or revoked.
403forbiddenThe key is valid but isn't allowed to make this request.
404not_foundThe object doesn't exist in this workspace, or the endpoint doesn't exist.
422validationThe request body or a query parameter is invalid, for example a malformed email or a user ID another contact already has. message says what to fix.
429rate_limitedToo many requests. Wait for the number of seconds in the Retry-After header, then try again.

Rate limits

Each API key can make 120 requests per minute. Every response tells you where you stand:

HeaderDescription
X-RateLimit-LimitRequests allowed per minute for this key (120).
X-RateLimit-RemainingRequests left in the current minute.
Retry-AfterSent with a 429 response: seconds to wait before retrying.
HTTP
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 17
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0

{"error": {"code": "rate_limited", "message": "Too many requests."}}

For bulk jobs such as a contact import, keep an eye on X-RateLimit-Remaining and pause when it reaches 0, rather than waiting for a 429.

Me

GET/api/v1/me

Returns details of the workspace your API key belongs to. Use it to check that a key works before you use it for anything else.

Shell
curl https://talkingdot.com/api/v1/me \
  -H "Authorization: Bearer cn_live_xxxxxxxx"

Contacts

A contact is anyone who has used your messenger or whom you've added: an anonymous visitor, a lead who has given contact details such as an email, or a user identified with your own user ID (external_id). See the contact object.

List contacts

GET/api/v1/contacts

Returns a page of contacts. Combine the filters to narrow the list.

Query parameterTypeDescription
emailstringOnly contacts with this email address.
external_idstringOnly the contact with this user ID (the user_id you pass to the messenger).
typestringvisitor, lead or user.
limit, cursorSee Pagination.
Shell
curl "https://talkingdot.com/api/v1/contacts?email=ada@example.com" \
  -H "Authorization: Bearer cn_live_xxxxxxxx"
JSON
{
  "data": [
    {
      "id": 1842,
      "type": "user",
      "external_id": "42",
      "name": "Ada Lovelace",
      "email": "ada@example.com",
      "phone": "+44 20 7946 0958",
      "company": "Analytical Engines Ltd",
      "avatar_url": null,
      "verified": true,
      "location": {
        "country": "GB",
        "region": "England",
        "city": "London",
        "timezone": "Europe/London"
      },
      "language": "en",
      "browser": "Chrome",
      "os": "macOS",
      "device": "desktop",
      "custom_attributes": {
        "plan": "pro",
        "seats": 5
      },
      "tags": [
        { "id": 3, "name": "vip" }
      ],
      "sessions": 12,
      "page_views": 87,
      "last_page_url": "https://www.example.com/pricing",
      "unsubscribed": false,
      "blocked": false,
      "first_seen_at": "2026-09-14T08:02:11Z",
      "last_seen_at": "2026-10-03T09:15:00Z",
      "created_at": "2026-09-14T08:02:11Z",
      "updated_at": "2026-10-03T09:15:00Z"
    }
  ],
  "meta": {
    "next_cursor": null
  }
}

Create a contact

POST/api/v1/contacts

Adds a contact, for example when someone signs up in your product. Returns 201 Created and the new contact. A contact with an external_id is a user; one with only contact details is a lead.

Body fieldTypeDescription
external_idstringYour own ID for this user: the same value you pass as user_id to the messenger. Must be unique in the workspace.
emailstringEmail address. Must be valid.
namestringFull name.
phonestringPhone number.
companystringCompany or organisation name.
custom_attributesobjectKey/value pairs with string, number or boolean values, for example {"plan": "pro"}.
Shell
curl https://talkingdot.com/api/v1/contacts \
  -H "Authorization: Bearer cn_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "43",
    "email": "charles@example.com",
    "name": "Charles Babbage",
    "company": "Analytical Engines Ltd",
    "custom_attributes": { "plan": "pro", "seats": 5 }
  }'
JSON
{
  "data": {
    "id": 1907,
    "type": "user",
    "external_id": "43",
    "name": "Charles Babbage",
    "email": "charles@example.com",
    "phone": null,
    "company": "Analytical Engines Ltd",
    "avatar_url": null,
    "verified": false,
    "location": {
      "country": null,
      "region": null,
      "city": null,
      "timezone": null
    },
    "language": null,
    "browser": null,
    "os": null,
    "device": null,
    "custom_attributes": {
      "plan": "pro",
      "seats": 5
    },
    "tags": [],
    "sessions": 0,
    "page_views": 0,
    "last_page_url": null,
    "unsubscribed": false,
    "blocked": false,
    "first_seen_at": "2026-10-03T09:20:00Z",
    "last_seen_at": "2026-10-03T09:20:00Z",
    "created_at": "2026-10-03T09:20:00Z",
    "updated_at": "2026-10-03T09:20:00Z"
  }
}

Retrieve a contact

GET/api/v1/contacts/{id}

Returns one contact.

Shell
curl https://talkingdot.com/api/v1/contacts/1842 \
  -H "Authorization: Bearer cn_live_xxxxxxxx"

Update a contact

PATCH/api/v1/contacts/{id}

Changes a contact. Takes the same fields as Create a contact; only the fields you send are changed. custom_attributes are merged into the existing ones: keys you send are added or overwritten, and a key set to null is removed. Returns the updated contact.

Shell
curl -X PATCH https://talkingdot.com/api/v1/contacts/1842 \
  -H "Authorization: Bearer cn_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "company": "Difference Engines plc",
    "custom_attributes": { "plan": "enterprise", "seats": 25 }
  }'

Delete a contact

DELETE/api/v1/contacts/{id}

Permanently erases the contact and everything stored about them, including all of their conversations and messages, events, notes and page views. Use it for GDPR erasure requests. This can't be undone. A contact.deleted webhook is sent.

Shell
curl -X DELETE https://talkingdot.com/api/v1/contacts/1842 \
  -H "Authorization: Bearer cn_live_xxxxxxxx"

Contact events

POST/api/v1/contacts/{id}/events

Records something the contact did, such as paying an invoice or finishing onboarding. Events appear in the contact sidebar in the Inbox. This is the server-side version of TalkingDot('track', …) in the JavaScript API.

Body fieldTypeDescription
namestringRequired. The event name. Letters, numbers, spaces and . _ - :, up to 80 characters.
metaobjectOptional details: up to 20 flat values (strings, numbers or booleans).
Shell
curl https://talkingdot.com/api/v1/contacts/1842/events \
  -H "Authorization: Bearer cn_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "invoice_paid",
    "meta": { "invoice": "INV-1042", "amount": 49, "currency": "EUR" }
  }'

Conversations

A conversation is a thread between a contact and your team. See the conversation object.

List conversations

GET/api/v1/conversations

Returns a page of conversations, without their messages.

Query parameterTypeDescription
statusstringopen, snoozed or closed.
assignee_idintegerOnly conversations assigned to this teammate.
contact_idintegerOnly this contact's conversations.
limit, cursorSee Pagination.
Shell
curl "https://talkingdot.com/api/v1/conversations?status=open&assignee_id=7" \
  -H "Authorization: Bearer cn_live_xxxxxxxx"
JSON
{
  "data": [
    {
      "id": 5531,
      "number": 318,
      "contact_id": 1842,
      "status": "open",
      "priority": false,
      "assignee_id": 7,
      "team_id": 2,
      "channel": "chat",
      "started_by": "contact",
      "subject": null,
      "source_url": "https://www.example.com/pricing",
      "tags": [
        { "id": 5, "name": "billing" }
      ],
      "rating": null,
      "first_response_seconds": 94,
      "snoozed_until": null,
      "waiting_since": "2026-10-03T09:15:00Z",
      "last_message_at": "2026-10-03T09:15:00Z",
      "created_at": "2026-10-03T09:01:30Z",
      "closed_at": null,
      "url": "https://talkingdot.com/app/12/inbox/5531"
    }
  ],
  "meta": {
    "next_cursor": null
  }
}

Retrieve a conversation

GET/api/v1/conversations/{id}

Returns one conversation with its messages. Check each message's type: message for replies, note for internal notes only your team sees, and event for timeline entries.

Shell
curl https://talkingdot.com/api/v1/conversations/5531 \
  -H "Authorization: Bearer cn_live_xxxxxxxx"
JSON
{
  "data": {
    "id": 5531,
    "number": 318,
    "contact_id": 1842,
    "status": "open",
    "priority": false,
    "assignee_id": 7,
    "team_id": 2,
    "channel": "chat",
    "started_by": "contact",
    "subject": null,
    "source_url": "https://www.example.com/pricing",
    "tags": [
      { "id": 5, "name": "billing" }
    ],
    "rating": null,
    "first_response_seconds": 94,
    "snoozed_until": null,
    "waiting_since": "2026-10-03T09:15:00Z",
    "last_message_at": "2026-10-03T09:15:00Z",
    "created_at": "2026-10-03T09:01:30Z",
    "closed_at": null,
    "url": "https://talkingdot.com/app/12/inbox/5531",
    "messages": [
      {
        "id": 88190,
        "conversation_id": 5531,
        "type": "message",
        "author_type": "contact",
        "author": { "id": 1842 },
        "body": "Hi! Can I switch my plan to annual billing?",
        "attachments": [],
        "via": "widget",
        "created_at": "2026-10-03T09:01:30Z"
      },
      {
        "id": 88197,
        "conversation_id": 5531,
        "type": "message",
        "author_type": "agent",
        "author": { "id": 7, "name": "Grace Hopper", "email": "grace@example.com" },
        "body": "Hi Ada, yes you can. Here's the invoice for the annual plan.",
        "attachments": [
          {
            "name": "invoice-1042.pdf",
            "url": "https://talkingdot.com/files/901/3f9c2a7b1e/invoice-1042.pdf",
            "size": 48213,
            "content_type": "application/pdf"
          }
        ],
        "via": "web",
        "created_at": "2026-10-03T09:03:04Z"
      },
      {
        "id": 88213,
        "conversation_id": 5531,
        "type": "message",
        "author_type": "contact",
        "author": { "id": 1842 },
        "body": "Thanks! I've just paid it.",
        "attachments": [],
        "via": "widget",
        "created_at": "2026-10-03T09:15:00Z"
      }
    ]
  }
}

Start a conversation

POST/api/v1/conversations

Starts an outbound conversation: a new conversation with a contact, opened by a message from your team. Returns the new conversation.

Body fieldTypeDescription
contact_idintegerRequired. The contact to write to.
bodystringRequired. The first message.
teammate_idintegerOptional. The teammate the message is sent as.
Shell
curl https://talkingdot.com/api/v1/conversations \
  -H "Authorization: Bearer cn_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": 1842,
    "body": "Hi Ada, your annual plan is active. Anything else we can help with?",
    "teammate_id": 7
  }'

Update a conversation

PATCH/api/v1/conversations/{id}

Changes a conversation's status, assignment, priority or tags. Send only the fields you want to change. Returns the updated conversation.

Body fieldTypeDescription
statusstringopen, snoozed or closed.
assignee_idinteger or nullThe teammate to assign, or null to unassign.
team_idinteger or nullThe team to assign, or null to remove the team.
prioritybooleantrue marks the conversation as priority, false clears it.
snoozed_untilstringWhen a snoozed conversation should reopen, as an ISO 8601 timestamp. Use it with "status": "snoozed".
tagsarray of stringsThe conversation's tags, by name, for example ["billing", "annual-plan"].
Shell
# Assign to a teammate and team, mark as priority and tag it
curl -X PATCH https://talkingdot.com/api/v1/conversations/5531 \
  -H "Authorization: Bearer cn_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "assignee_id": 7,
    "team_id": 2,
    "priority": true,
    "tags": ["billing", "annual-plan"]
  }'

# Snooze until Monday morning
curl -X PATCH https://talkingdot.com/api/v1/conversations/5531 \
  -H "Authorization: Bearer cn_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "status": "snoozed", "snoozed_until": "2026-10-05T08:00:00Z" }'

# Close it
curl -X PATCH https://talkingdot.com/api/v1/conversations/5531 \
  -H "Authorization: Bearer cn_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "status": "closed" }'

Replies and notes

Add messages to an existing conversation, either from your team or on behalf of the contact. Each message is a message object.

Reply as a teammate

POST/api/v1/conversations/{id}/reply

Sends a reply the contact sees, or adds an internal note only your team sees. Returns the new message.

Body fieldTypeDescription
bodystringRequired. The text of the reply or note.
typestringRequired. comment for a reply to the contact, or note for an internal note.
teammate_idintegerOptional. The teammate the reply or note is written as.
Shell
curl https://talkingdot.com/api/v1/conversations/5531/reply \
  -H "Authorization: Bearer cn_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "comment",
    "body": "Payment received, thank you! Your plan is now billed annually.",
    "teammate_id": 7
  }'
JSON
{
  "data": {
    "id": 88220,
    "conversation_id": 5531,
    "type": "message",
    "author_type": "agent",
    "author": { "id": 7, "name": "Grace Hopper", "email": "grace@example.com" },
    "body": "Payment received, thank you! Your plan is now billed annually.",
    "attachments": [],
    "via": "api",
    "created_at": "2026-10-03T09:18:42Z"
  }
}

To leave a note for your team instead, send "type": "note". The contact never sees notes.

Reply as the contact

POST/api/v1/conversations/{id}/contact-reply

Adds a message from the conversation's contact, as if they had written it themselves. Use it to bring in messages the contact sent through your own app or another channel.

Body fieldTypeDescription
bodystringRequired. The contact's message.
Shell
curl https://talkingdot.com/api/v1/conversations/5531/contact-reply \
  -H "Authorization: Bearer cn_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "body": "Great, thanks for the quick help!" }'

Teammates and teams

List teammates

GET/api/v1/teammates

Returns the people in your workspace. Use a teammate's id as assignee_id or teammate_id. role is owner, admin or agent; availability is online or away.

Shell
curl https://talkingdot.com/api/v1/teammates \
  -H "Authorization: Bearer cn_live_xxxxxxxx"
JSON
{
  "data": [
    {
      "id": 7,
      "name": "Grace Hopper",
      "email": "grace@example.com",
      "role": "admin",
      "availability": "online",
      "avatar_url": null
    }
  ],
  "meta": {
    "next_cursor": null
  }
}

List teams

GET/api/v1/teams

Returns your workspace's teams. Use a team's id as team_id when you update a conversation.

Shell
curl https://talkingdot.com/api/v1/teams \
  -H "Authorization: Bearer cn_live_xxxxxxxx"

Tags

GET/api/v1/tags

Returns your workspace's tags, each with an id and a name. Tag conversations by name with PATCH /api/v1/conversations/{id}.

Shell
curl https://talkingdot.com/api/v1/tags \
  -H "Authorization: Bearer cn_live_xxxxxxxx"

Help center

Manage the articles in your help center. Articles are grouped into collections and are either draft or published.

List collections

GET/api/v1/collections

Returns your help-center collections. Use a collection's id as an article's collection_id.

Shell
curl https://talkingdot.com/api/v1/collections \
  -H "Authorization: Bearer cn_live_xxxxxxxx"

List articles

GET/api/v1/articles

Returns a page of articles.

Query parameterTypeDescription
statusstringdraft or published.
limit, cursorSee Pagination.
Shell
curl "https://talkingdot.com/api/v1/articles?status=published" \
  -H "Authorization: Bearer cn_live_xxxxxxxx"

Retrieve an article

GET/api/v1/articles/{id}

Returns one article, including its HTML body and its view and "was this helpful?" counts.

Shell
curl https://talkingdot.com/api/v1/articles/64 \
  -H "Authorization: Bearer cn_live_xxxxxxxx"
JSON
{
  "data": {
    "id": 64,
    "title": "How refunds work",
    "slug": "how-refunds-work",
    "excerpt": "Where to request a refund and what happens next.",
    "body_html": "<p>Open <strong>Billing</strong> and choose <strong>Request refund</strong>.</p>",
    "status": "published",
    "collection_id": 3,
    "views": 1204,
    "helpful": 87,
    "unhelpful": 6,
    "url": "https://talkingdot.com/help/acme/articles/64-how-refunds-work",
    "created_at": "2026-08-21T14:00:00Z",
    "updated_at": "2026-09-30T10:12:45Z"
  }
}

Create an article

POST/api/v1/articles

Adds an article. Returns the new article.

Body fieldTypeDescription
titlestringRequired. The article title.
body_htmlstringThe article body as HTML.
collection_idintegerThe collection to put it in.
statusstringdraft or published.
Shell
curl https://talkingdot.com/api/v1/articles \
  -H "Authorization: Bearer cn_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Switching to annual billing",
    "body_html": "<p>Open <strong>Billing</strong>, choose <strong>Change plan</strong> and pick <strong>Annual</strong>.</p>",
    "collection_id": 3,
    "status": "draft"
  }'

Update an article

PATCH/api/v1/articles/{id}

Changes an article. Takes the same fields as Create an article; send only the ones you want to change. Returns the updated article.

Shell
curl -X PATCH https://talkingdot.com/api/v1/articles/64 \
  -H "Authorization: Bearer cn_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "status": "published" }'

Delete an article

DELETE/api/v1/articles/{id}

Deletes an article. It disappears from your help center and from search in the messenger.

Shell
curl -X DELETE https://talkingdot.com/api/v1/articles/64 \
  -H "Authorization: Bearer cn_live_xxxxxxxx"

Object reference

The same objects are used in API responses and in webhook payloads. Timestamps are ISO 8601 in UTC; fields without a value are null.

Contact

FieldTypeDescription
idintegerUnique ID of the contact.
typestringvisitor (anonymous), lead (has given contact details, such as an email) or user (has an external_id).
external_idstring or nullYour own user ID: the user_id passed to the messenger, or set through the API.
namestring or nullFull name.
emailstring or nullEmail address, in lowercase.
phonestring or nullPhone number.
companystring or nullCompany or organisation.
avatar_urlstring or nullURL of the contact's picture.
verifiedbooleantrue when the identity was proven with a valid user_hash. See Identity verification.
locationobjectcountry (two-letter code, such as GB), region, city and timezone (such as Europe/London). Each can be null.
languagestring or nullBrowser language code, such as en.
browserstring or nullBrowser name, such as Chrome.
osstring or nullOperating system, such as macOS.
devicestring or nulldesktop, tablet or mobile.
custom_attributesobjectYour own key/value data. An empty object when there is none.
tagsarrayTags on the contact, each {"id", "name"}.
sessionsintegerNumber of visits.
page_viewsintegerNumber of pages viewed.
last_page_urlstring or nullThe last page the contact viewed.
unsubscribedbooleantrue when the contact has unsubscribed from emails.
blockedbooleantrue when your team has blocked the contact.
first_seen_attimestampWhen the contact was first seen.
last_seen_attimestampWhen the contact was last seen.
created_attimestampWhen the contact was created.
updated_attimestamp or nullWhen the contact's details last changed.

Conversation

FieldTypeDescription
idintegerUnique ID of the conversation.
numberintegerThe conversation's number within your workspace.
contact_idintegerThe contact the conversation is with.
statusstringopen, snoozed or closed.
prioritybooleantrue when marked as priority.
assignee_idinteger or nullThe assigned teammate.
team_idinteger or nullThe assigned team.
channelstringchat, email or api.
started_bystringWho started the conversation: contact, agent, auto (an auto message) or api.
subjectstring or nullSubject line, mainly for email conversations.
source_urlstring or nullThe page the conversation started on.
tagsarrayTags on the conversation, each {"id", "name"}.
ratingobject or nullThe contact's rating: score (1 to 5), comment and rated_at. null until they rate.
first_response_secondsinteger or nullSeconds from the start of the conversation to your team's first reply.
snoozed_untiltimestamp or nullWhen a snoozed conversation reopens.
waiting_sincetimestamp or nullWhen the contact started waiting for a reply; null when your team has replied.
last_message_attimestamp or nullTime of the latest message.
created_attimestampWhen the conversation started.
closed_attimestamp or nullWhen it was closed.
urlstringLink to the conversation in the Inbox (for teammates).

Message

FieldTypeDescription
idintegerUnique ID of the message.
conversation_idintegerThe conversation it belongs to.
typestringmessage (seen by the contact), note (internal) or event (a timeline entry, such as an assignment).
author_typestringcontact, agent (a teammate), bot or system.
authorobject or nullFor a teammate: id, name and email. For the contact: id. null for bot and system messages.
bodystringThe message text.
attachmentsarrayFiles, each with name, url, size (bytes) and content_type.
viastringWhere it was written: widget (the messenger), web (the Inbox), email, api, bot or auto.
created_attimestampWhen it was sent.
Last updated October 4, 2026 Something unclear? Tell us