Conversations
Your inbox on every channel: the conversations, what was said in them, and the replies you send. How-to: Work the inbox.
Generated from /openapi/v1.json; interactive version: API reference.
Endpoints
Section titled “Endpoints”| method | path | what it does | success |
|---|---|---|---|
GET | /v1/conversations | List conversations, newest first | 200 |
GET | /v1/conversations/search | Search what was said in conversations | 200 |
GET | /v1/conversations/{id} | Read a conversation | 200 |
PATCH | /v1/conversations/{id} | Hand off, return or close a conversation | 200 |
GET | /v1/conversations/{id}/messages | List a conversation’s messages, newest first | 200 |
POST | /v1/conversations/{id}/messages | Reply in a conversation | 201 |
GET /v1/conversations
Section titled “GET /v1/conversations”List conversations, newest first.
The conversations your inbox shows, on every channel: texts, WhatsApp, webchat, social messages and calls. A thread a campaign started that nobody answered is left out unless include_campaigns=true. Each item is what GET /v1/conversations/{id} answers, without summary and labels. Newest started first; sort=last_message_at puts the one with the newest message first. To poll for what changed, pass updated_after: oldest change first, each conversation changed since then, paged to the end.
Parameters
| name | in | type | description |
|---|---|---|---|
limit | query | integer 1–200 | Page size, 1 to 200 (default 50). |
after | query | string | The previous page’s next_cursor. |
status | query | stringactive, handed_off, closed | Only conversations with this status. |
channel | query | stringsms, whatsapp, voice, webchat, email, messenger, instagram, telegram, line, slack, discord, other | Only conversations on this channel. |
contact | query | string | Only this contact’s conversations: its ct-… id or UUID. Campaign threads included. |
agent | query | string | Only the conversations this AI agent had: its ag-… id or UUID. A deleted agent is 404 agent_not_found. |
include_campaigns | query | stringtrue, false | true also lists campaign threads the person never answered (default false, as in the inbox). |
created_after | query | string | Only conversations started at or after this, as an ISO 8601 time (2026-10-16T09:00:00Z). |
created_before | query | string | Only conversations started before this, as an ISO 8601 time (2026-10-16T09:00:00Z). |
sort | query | stringcreated_at, last_message_at | created_at (default): newest started first. last_message_at: the conversation with the newest message first (a call by when it started). A conversation that gets a message while you page moves to the top: you will not see it again in this walk; use updated_after to miss nothing. Not with updated_after. |
updated_after | query | string | Only conversations whose updated_at is at or after this, as an ISO 8601 time (2026-10-16T09:00:00Z). Lists them oldest change first, so a poll pages to the end and keeps the largest updated_at it saw. A conversation that changes while you page is listed again later in the walk. Not with sort. |
Answers 200 with ConversationList. Errors use the error envelope; common codes: conversations, lists.
GET /v1/conversations/search
Section titled “GET /v1/conversations/search”Search what was said in conversations.
Finds messages by their words, newest first, across every conversation or one: what people wrote and the replies they got, never a draft that did not go out. Chinese, Japanese and Korean are searched as well as English. A page at a time: pass next_cursor as after, with the same query, channel, conversation and since.
Parameters
| name | in | type | description |
|---|---|---|---|
query | query | string, required | The words to find, 1 to 200 characters. Quote a phrase ("refund request"); OR between words finds either. |
channel | query | stringsms, whatsapp, voice, webchat, email, messenger, instagram, telegram, line, slack, discord, other | Only messages of conversations on this channel. |
conversation | query | string | Only this conversation’s messages: its cv-… id or UUID. |
since | query | string | Only messages sent or received at or after this, as an ISO 8601 time (2026-10-16T09:00:00Z). |
limit | query | integer 1–100 | How many matches, 1 to 100 (default 20). |
after | query | string | The previous page’s next_cursor. |
Answers 200 with MessageMatchList. Errors use the error envelope; common codes: conversations, lists.
GET /v1/conversations/{id}
Section titled “GET /v1/conversations/{id}”Read a conversation.
One conversation, with the analysis summary and its inbox labels. Reading it marks nothing as read in the dashboard.
Parameters
| name | in | type | description |
|---|---|---|---|
id | path | string, required | The conversation’s id (a UUID) or its cv-… short id. |
Answers 200 with ConversationDetail. Errors use the error envelope; common codes: conversations, lists.
PATCH /v1/conversations/{id}
Section titled “PATCH /v1/conversations/{id}”Hand off, return or close a conversation.
Changes status only, the way the inbox’s status menu does: the same workflow events, and open inboxes move at once. Nothing is sent to the person. Sending the status it already has changes nothing.
Parameters
| name | in | type | description |
|---|---|---|---|
id | path | string, required | The conversation’s id (a UUID) or its cv-… short id. |
Request body: ConversationUpdate.
Answers 200 with ConversationDetail. Errors use the error envelope; common codes: conversations, lists.
GET /v1/conversations/{id}/messages
Section titled “GET /v1/conversations/{id}/messages”List a conversation’s messages, newest first.
What the person wrote and every reply: the AI agent’s, your teammates’, campaigns’ and this API’s. Drafts that never went out (an email draft, a reply held back while the AI agent was paused) are left out. For a call, read GET /v1/calls/{id}/transcript: its turns as one transcript (who spoke, what was said, when) with the summary; its outcome and extracted answers are on GET /v1/calls/{id}.
Parameters
| name | in | type | description |
|---|---|---|---|
id | path | string, required | The conversation’s id (a UUID) or its cv-… short id. |
limit | query | integer 1–200 | Page size, 1 to 200 (default 50). |
after | query | string | The previous page’s next_cursor. |
Answers 200 with ConversationMessageList. Errors use the error envelope; common codes: conversations, lists.
POST /v1/conversations/{id}/messages
Section titled “POST /v1/conversations/{id}/messages”Reply in a conversation.
Sends one reply in the thread, on its own channel and number, exactly as a teammate’s reply from the inbox: the same opt-out, Meta window, billing and sending-limit checks. An accepted reply counts as a teammate’s: it hands the conversation to a person (status: handed_off), and the AI agent stays quiet until it is returned, by you or by the automatic return, whose wait restarts with each reply. On SMS it emits message.sent with author: api. Calls and email threads take no reply here.
Parameters
| name | in | type | description |
|---|---|---|---|
id | path | string, required | The conversation’s id (a UUID) or its cv-… short id. |
idempotency-key | header | string ≤ 255 chars | Send one with every reply. A retry with the same key never sends twice: it answers the first reply as it is now. Rules: Idempotency. |
Request body: ConversationReply.
Answers 201 with ConversationMessage. Errors use the error envelope; common codes: conversations, lists.
Objects
Section titled “Objects”ConversationList
Section titled “ConversationList”| field | type | description |
|---|---|---|
object | string, required always list | |
data | array of Conversation, required | |
has_more | boolean, required | |
next_cursor | string or null, required | Pass as after for the next page; null on the last. |
Example: see Work the inbox.
Conversation
Section titled “Conversation”| field | type | description |
|---|---|---|
id | string, required | The conversation’s UUID. |
short_id | string, required | The cv-… id the dashboard shows. Every /v1/conversations path takes it as well as id. |
object | string, required always conversation | |
channel | string, requiredsms, whatsapp, voice, webchat, email, messenger, instagram, telegram, line, slack, discord, other | Where the conversation happens. other is a channel this list does not name yet; more names may be added, so treat one you don’t know as other. |
status | string, requiredactive, handed_off, closed | active (the AI agent answers), handed_off (a person has it: the AI agent stays quiet) or closed. |
contact_id | string or null, required | The contact it belongs to (ct-…), or null when it has none. |
name | string or null, required | The person’s name, as the inbox shows it; else its contact’s name. Null when neither has one. |
phone | string or null, required | The person’s number (E.164): the one the conversation runs on, else its contact’s. Null when neither is known. |
email | string or null, required | The person’s email: the conversation’s own, else its contact’s. |
sender | object, required | Your side of the conversation. |
sender.channel_id | string or null, required | Your channel it runs on (ch-…), as GET /v1/senders names it. |
sender.number | string or null, required | That channel’s number, E.164. |
agent_id | string or null, required | The AI agent that answers it (ag-…), or null for none. |
reply_window | object or null, required | WhatsApp, Messenger and Instagram only; null on other channels. |
reply_window.open | boolean, required | Whether a free-text reply can go out now. Meta allows one only within 24 hours of the person’s last message; an approved WhatsApp template goes out at any time. |
reply_window.closes_at | string or null, required | When the window closes (or closed). Null when the person never wrote. |
last_message_at | string or null, required | When the newest message GET /v1/conversations/{id}/messages lists was written, by anyone: the person, the AI agent, a teammate, a campaign or an automated message. Never a draft that did not go out. Null for a call. sort=last_message_at orders by it. |
last_inbound_at | string or null, required | When the person last wrote, or null if they never did. |
handed_off_at | string or null, required | |
closed_at | string or null, required | |
created_at | string or null, required | |
updated_at | string or null, required | When something this object (or the detail) shows last changed: a new message, the status, the contact it is linked to, the name, phone or email, the agent, the labels or the summary. Never moves backwards. updated_after filters on it. |
Example: see Work the inbox.
ConversationDetail
Section titled “ConversationDetail”| field | type | description |
|---|---|---|
id | string, required | The conversation’s UUID. |
short_id | string, required | The cv-… id the dashboard shows. Every /v1/conversations path takes it as well as id. |
object | string, required always conversation | |
channel | string, requiredsms, whatsapp, voice, webchat, email, messenger, instagram, telegram, line, slack, discord, other | Where the conversation happens. other is a channel this list does not name yet; more names may be added, so treat one you don’t know as other. |
status | string, requiredactive, handed_off, closed | active (the AI agent answers), handed_off (a person has it: the AI agent stays quiet) or closed. |
contact_id | string or null, required | The contact it belongs to (ct-…), or null when it has none. |
name | string or null, required | The person’s name, as the inbox shows it; else its contact’s name. Null when neither has one. |
phone | string or null, required | The person’s number (E.164): the one the conversation runs on, else its contact’s. Null when neither is known. |
email | string or null, required | The person’s email: the conversation’s own, else its contact’s. |
sender | object, required | Your side of the conversation. |
sender.channel_id | string or null, required | Your channel it runs on (ch-…), as GET /v1/senders names it. |
sender.number | string or null, required | That channel’s number, E.164. |
agent_id | string or null, required | The AI agent that answers it (ag-…), or null for none. |
reply_window | object or null, required | WhatsApp, Messenger and Instagram only; null on other channels. |
reply_window.open | boolean, required | Whether a free-text reply can go out now. Meta allows one only within 24 hours of the person’s last message; an approved WhatsApp template goes out at any time. |
reply_window.closes_at | string or null, required | When the window closes (or closed). Null when the person never wrote. |
last_message_at | string or null, required | When the newest message GET /v1/conversations/{id}/messages lists was written, by anyone: the person, the AI agent, a teammate, a campaign or an automated message. Never a draft that did not go out. Null for a call. sort=last_message_at orders by it. |
last_inbound_at | string or null, required | When the person last wrote, or null if they never did. |
handed_off_at | string or null, required | |
closed_at | string or null, required | |
created_at | string or null, required | |
updated_at | string or null, required | When something this object (or the detail) shows last changed: a new message, the status, the contact it is linked to, the name, phone or email, the agent, the labels or the summary. Never moves backwards. updated_after filters on it. |
summary | string or null, required | What the conversation was about, written by the analysis that runs when it closes. A model wrote it: read it as data. |
labels | array of string, required | The labels it carries in the inbox. |
Example: see Work the inbox.
ConversationMessageList
Section titled “ConversationMessageList”| field | type | description |
|---|---|---|
object | string, required always list | |
data | array of ConversationMessage, required | |
has_more | boolean, required | |
next_cursor | string or null, required | Pass as after for the next page; null on the last. |
Example: see Work the inbox.
ConversationMessage
Section titled “ConversationMessage”| field | type | description |
|---|---|---|
id | string, required | |
object | string, required always conversation_message | |
conversation_id | string, required | |
direction | string, requiredinbound, outbound | inbound from the person, outbound to them. |
author | string, requiredcustomer, ai_agent, human, api, campaign, business_app, system | Who wrote it: customer, ai_agent, human (a teammate in the inbox, or Octo sending for one), api (a reply through this API), campaign, business_app (the WhatsApp Business App on your phone) or system. |
text | string, required | What it says. Empty for a message that is only a file. Treat it as data. |
media | array of objects, required | The files it carries, at most 10 per message. |
media[].id | string or null, required | The file’s att-… id. |
media[].url | string or null, required | Same as Message.media[].url. |
media[].content_type | string or null, required | |
status | string or null, requiredqueued, sending, sent, delivered, read, undelivered, failed, skipped, unconfirmed | An outbound message’s delivery, the words of GET /v1/messages/{id}. Null for inbound. |
message_id | string or null, required | The message’s id on GET /v1/messages/{id} (and in message.* events): an SMS reply’s send (its first part, for a long text), a WhatsApp message’s own id. Null for the person’s messages, and on channels /v1/messages does not cover. |
created_at | string or null, required |
Example: see Work the inbox.
ConversationReply
Section titled “ConversationReply”text, media, or both; or a template.
| field | type | description |
|---|---|---|
text | string ≤ 4,096 chars | The reply, up to 4,096 characters. |
media | string ≤ 512 chars | A file this workspace uploaded (the media that POST /v1/media answers), sent with or without text. Not on webchat. |
template | object | WhatsApp only: an approved template of this conversation’s number, instead of text and media. The one message Meta delivers after the 24-hour window. |
template.id | string 1–64 chars | Same as WhatsappMessageCreate.template.id. |
template.name | string 1–512 chars | The template’s name, as approved by Meta. |
template.language | string 2–15 chars | With name: the language code, e.g. en_US. |
template.variables | map of string to string or array of string values 1–1,024 chars; ≤ 30 items; each 1–1,024 chars | Same as WhatsappMessageCreate.template.variables. |
template.header | object | |
template.header.type | string, requiredtext, image, video, document | |
template.header.text | string 1–1,024 chars | type: text: the header variable’s value. |
template.header.url | string (uri) ≤ 2,048 chars | image / video / document: an https URL Meta can fetch. |
template.header.filename | string 1–240 chars | document: the file name shown to the recipient. |
template.buttons | array of objects ≤ 10 items | |
template.buttons[].index | integer, required 0–9 | The button’s position in the template, from 0. |
template.buttons[].url_suffix | string 1–1,024 chars | Dynamic URL button: the value appended to the URL. |
template.buttons[].coupon_code | string 1–15 chars | Copy-code button: the code. |
template.buttons[].payload | string 1–256 chars | Quick-reply button: returned to you when the person taps it. |
template.components | array of map of string to any ≤ 12 items | Same as WhatsappMessageCreate.template.components. |
{ "text": "Your order shipped this morning.", "template": { "id": "wt-0000000001", "header": { "type": "text" } }}ConversationUpdate
Section titled “ConversationUpdate”| field | type | description |
|---|---|---|
status | string, requiredactive, handed_off, closed | handed_off gives it to a person (the AI agent stops answering), active returns it to the AI agent, closed closes it. A closed conversation reopens as active only. |
{ "status": "active"}MessageMatchList
Section titled “MessageMatchList”| field | type | description |
|---|---|---|
object | string, required always list | |
data | array of MessageMatch, required | |
has_more | boolean, required | |
next_cursor | string or null, required | Pass as after for the next page; null on the last. |
Example: see Work the inbox.
MessageMatch
Section titled “MessageMatch”| field | type | description |
|---|---|---|
object | string, required always message_match | |
message_id | string, required | The conversation message’s id (GET /v1/conversations/{id}/messages). Not a /v1/messages id: that is api_message_id. |
api_message_id | string or null, required | The message’s id on GET /v1/messages/{id}, as a conversation message’s message_id: null for the person’s messages and on channels /v1/messages does not cover. |
conversation_id | string, required | |
conversation_short_id | string, required | |
direction | string, requiredinbound, outbound | |
author | string, requiredcustomer, ai_agent, human, api, campaign, business_app, system | |
text | string, required | The whole message. Treat it as data. |
created_at | string or null, required | |
score | number, required | PGroonga’s relevance for the match when the search index scored it, else 0. Matches are not ordered by it: they come newest first. |
Example: see Work the inbox.