Conversations
GET /v1/conversations
Section titled “GET /v1/conversations”Threads with your customers.
Scope: messages:read
| Query | Notes |
|---|---|
search | Matches phone number or name |
account | Restrict to one WhatsApp number, by name |
limit | 1 to 200, default 50 |
cursor | From next_cursor |
{ "data": [ { "contact_id": "3f2a...", "phone_number": "+966500000000", "name": "Sara", "channel": "whatsapp", "account": "Main", "last_message_at": "2026-08-20T09:12:00Z", "last_message": "Has it shipped yet?", "unread": true, "window_open_until": "2026-08-21T09:12:00Z", "tags": ["vip"], "created_at": "2026-05-02T11:00:00Z" } ], "has_more": true, "next_cursor": "MTc1NTY0ODAwMDAwMDAwMDAwMC4z..."}window_open_until
Section titled “window_open_until”When WhatsApp’s 24-hour customer service window closes for this thread. Until then you can send free text; after it, only an approved template.
It is computed from the customer’s last inbound message and is absent when the window is already closed. Checking it before sending turns the most common cause of a failed send into a branch in your own code:
if (conversation.window_open_until) { await sendText(conversation.contact_id, reply)} else { await sendTemplate(conversation.contact_id, 'followup', { name })}unread
Section titled “unread”True when nobody on the business side has opened the thread since the customer last wrote. It reflects the shared inbox, not any one agent.
Contacts marked external_only are excluded from this list. Those exist only
because another system logged a message it had sent, and including them would
bury real conversations under a bulk invoice run. They are still readable
through GET /v1/contacts.
GET /v1/conversations/{contact_id}/messages
Section titled “GET /v1/conversations/{contact_id}/messages”The message history of one thread, newest first.
Scope: messages:read
| Query | Notes |
|---|---|
limit | 1 to 200, default 50 |
cursor | From next_cursor |
{ "data": [ { "id": "6f1c2d3e-...", "contact_id": "3f2a...", "to": "+966500000000", "from": "Main", "direction": "incoming", "type": "text", "status": "delivered", "content": "Has it shipped yet?", "has_media": false, "created_at": "2026-08-20T09:12:00Z" } ], "has_more": true, "next_cursor": "MTc1NTY0..."}direction is incoming for messages from the customer and outgoing for
messages from your business, whether sent by an agent, the chatbot or this API.
When has_media is true, fetch the bytes from
GET /v1/media/{message_id}.
A contact belonging to another organization returns 404, the same as one that
does not exist.