List Conversations
Returns a paginated list of all conversations for a given chatbot.
GET /api/dev/v1/chatbots/{chatbot_id}/conversations Authorization: Bearer <api-key>GET /api/dev/v1/chatbots/{chatbot_id}/conversations Authorization: Bearer <api-key>
Query Parameters
Filters
statusstringoptionalFilter by conversation status. One of: ACTIVE, CLOSED, ARCHIVED, HANDOFF_PENDING, AGENT_ACTIVE.
channelstringoptionalFilter by channel (e.g. whatsapp, web, sms, api).
start_timestringoptionalFilter conversations created at or after this time (ISO 8601, e.g. 2026-01-01T00:00:00Z).
end_timestringoptionalFilter conversations created at or before this time (ISO 8601).
Pagination
limitintegeroptionaldefault: 50Page size. Min 1, max 67.
cursorstringoptionalPagination cursor from a previous response.
curl -X GET "https://developers.sarufi.io/api/dev/v1/chatbots/<chatbot-id>/conversations?status=ACTIVE&channel=whatsapp&limit=25" \ -H "Authorization: Bearer <your-api-key>"curl -X GET "https://developers.sarufi.io/api/dev/v1/chatbots/<chatbot-id>/conversations?status=ACTIVE&channel=whatsapp&limit=25" \ -H "Authorization: Bearer <your-api-key>"
Response - 200 OK
{ "count": 1, "conversations": [ { "id": "01JPQ...", "chatbot_id": "01JMXYZ...", "user_phone": "+255712345678", "contact_name": "John", "channel": "whatsapp", "status": "ACTIVE", "last_message_id": "01JRS...", "last_message_at": "2026-02-18T14:35:00Z", "last_message_preview": "Thanks for your help!", "unread_count": 2, "created_at": "2026-02-18T14:22:00Z", "updated_at": "2026-02-18T14:35:00Z" } ], "next_cursor": "eyJpZCI6IjAxSlBRIn0=" }{ "count": 1, "conversations": [ { "id": "01JPQ...", "chatbot_id": "01JMXYZ...", "user_phone": "+255712345678", "contact_name": "John", "channel": "whatsapp", "status": "ACTIVE", "last_message_id": "01JRS...", "last_message_at": "2026-02-18T14:35:00Z", "last_message_preview": "Thanks for your help!", "unread_count": 2, "created_at": "2026-02-18T14:22:00Z", "updated_at": "2026-02-18T14:35:00Z" } ], "next_cursor": "eyJpZCI6IjAxSlBRIn0=" }
Get Conversation
Retrieves a single conversation by ID.
GET /api/dev/v1/chatbots/{chatbot_id}/conversations/{conversation_id} Authorization: Bearer <api-key>GET /api/dev/v1/chatbots/{chatbot_id}/conversations/{conversation_id} Authorization: Bearer <api-key>
curl -X GET https://developers.sarufi.io/api/dev/v1/chatbots/<chatbot-id>/conversations/<conversation-id> \ -H "Authorization: Bearer <your-api-key>"curl -X GET https://developers.sarufi.io/api/dev/v1/chatbots/<chatbot-id>/conversations/<conversation-id> \ -H "Authorization: Bearer <your-api-key>"
Response - 200 OK
{ "id": "01JPQ...", "chatbot_id": "01JMXYZ...", "user_phone": "+255712345678", "contact_name": "John", "channel": "whatsapp", "status": "ACTIVE", "last_message_id": "01JRS...", "last_message_at": "2026-02-18T14:35:00Z", "last_message_preview": "Thanks for your help!", "unread_count": 2, "created_at": "2026-02-18T14:22:00Z", "updated_at": "2026-02-18T14:35:00Z" }{ "id": "01JPQ...", "chatbot_id": "01JMXYZ...", "user_phone": "+255712345678", "contact_name": "John", "channel": "whatsapp", "status": "ACTIVE", "last_message_id": "01JRS...", "last_message_at": "2026-02-18T14:35:00Z", "last_message_preview": "Thanks for your help!", "unread_count": 2, "created_at": "2026-02-18T14:22:00Z", "updated_at": "2026-02-18T14:35:00Z" }
Get Conversation Messages
Returns messages for a conversation, ordered oldest first.
GET /api/dev/v1/chatbots/{chatbot_id}/conversations/{conversation_id}/messages Authorization: Bearer <api-key>GET /api/dev/v1/chatbots/{chatbot_id}/conversations/{conversation_id}/messages Authorization: Bearer <api-key>
Query Parameters
Pagination
limitintegeroptionaldefault: 50Page size. Min 1, max 67.
cursorstringoptionalPagination cursor from a previous response.
curl -X GET "https://developers.sarufi.io/api/dev/v1/chatbots/<chatbot-id>/conversations/<conversation-id>/messages?limit=25" \ -H "Authorization: Bearer <your-api-key>"curl -X GET "https://developers.sarufi.io/api/dev/v1/chatbots/<chatbot-id>/conversations/<conversation-id>/messages?limit=25" \ -H "Authorization: Bearer <your-api-key>"
Response - 200 OK
{ "count": 2, "messages": [ { "id": "01JRS...", "conversation_id": "01JPQ...", "message": { "text": "Hello, I need help with my order" }, "response": { "text": "Hi! I'd be happy to help. What's your order number?" }, "message_type": "text", "responder": "BOT", "responder_name": null, "received_at": "2026-02-18T14:22:05Z", "responded_at": "2026-02-18T14:22:06Z", "status": "delivered", "created_at": "2026-02-18T14:22:05Z" } ], "next_cursor": null }{ "count": 2, "messages": [ { "id": "01JRS...", "conversation_id": "01JPQ...", "message": { "text": "Hello, I need help with my order" }, "response": { "text": "Hi! I'd be happy to help. What's your order number?" }, "message_type": "text", "responder": "BOT", "responder_name": null, "received_at": "2026-02-18T14:22:05Z", "responded_at": "2026-02-18T14:22:06Z", "status": "delivered", "created_at": "2026-02-18T14:22:05Z" } ], "next_cursor": null }
Send a Message to a Customer
Sends a message into an existing or new conversation on WhatsApp, Messenger or the web chat, outside of a chat session. Use it to answer from your own system, to follow up after an order, or to re-open a conversation with a WhatsApp template.
POST /api/dev/v1/chatbots/{chatbot_id}/messages Authorization: Bearer <api-key> X-Sarufi-Signature: t=<unix-seconds>,v1=<hmac-sha256> Content-Type: application/jsonPOST /api/dev/v1/chatbots/{chatbot_id}/messages Authorization: Bearer <api-key> X-Sarufi-Signature: t=<unix-seconds>,v1=<hmac-sha256> Content-Type: application/json
Every send is signed
Besides the API key, each request carries a signature made with the chatbot's webhook_secret (see Chatbots). Sign the exact bytes you send: v1 is the hex HMAC-SHA256 of "<t>.<raw body>", where t is the current Unix time in seconds. A signature older than five minutes is refused.
Request Body
Fields
channelstringrequiredWHATSAPP, MESSENGER or WEB.
tostringrequiredWho receives it. WhatsApp: the phone number in international format. Messenger: the page-scoped user id. Web: the conversation_id of the visitor's chat.
messageobjectrequiredOne message, chosen by its type. See Message shapes.
metadataobjectoptionalAnything you want stored with the message, for example your own order id.
sender_namestringoptionalThe person replying, when it is a person. Trimmed and cut at 60 characters. Without it the message is signed with your API key's name.
Message shapes for sending
type | Fields |
|---|---|
text | body |
buttons | body, buttons (up to 3, each a string or { id, title }), optional header, footer |
list | body, button_text, items (each { id, title, description?, section? }), optional header, footer |
image, video, audio, document | url, optional caption, filename for a document |
cta_url | body, display_text, url |
template | name, language (default en), optional parameters. WhatsApp only |
On WhatsApp a plain message is delivered only within 24 hours of the customer's last message. Outside that window send a template; a plain send is refused with window_closed.
BODY='{"channel":"WHATSAPP","to":"+255712345678","message":{"type":"text","body":"Your order has shipped."}}' T=$(date +%s) SIG=$(printf '%s.%s' "$T" "$BODY" | openssl dgst -sha256 -hmac "<webhook-secret>" | awk '{print $NF}') curl -X POST https://developers.sarufi.io/api/dev/v1/chatbots/<chatbot-id>/messages \ -H "Authorization: Bearer <your-api-key>" \ -H "X-Sarufi-Signature: t=$T,v1=$SIG" \ -H "Content-Type: application/json" \ --data "$BODY"BODY='{"channel":"WHATSAPP","to":"+255712345678","message":{"type":"text","body":"Your order has shipped."}}' T=$(date +%s) SIG=$(printf '%s.%s' "$T" "$BODY" | openssl dgst -sha256 -hmac "<webhook-secret>" | awk '{print $NF}') curl -X POST https://developers.sarufi.io/api/dev/v1/chatbots/<chatbot-id>/messages \ -H "Authorization: Bearer <your-api-key>" \ -H "X-Sarufi-Signature: t=$T,v1=$SIG" \ -H "Content-Type: application/json" \ --data "$BODY"
Response - 201 Created
{ "message_id": "wamid.HBgLM...", "status": "sent", "channel": "WHATSAPP", "conversation_id": "01JPQ...", "responder": "HUMAN", "held": true }{ "message_id": "wamid.HBgLM...", "status": "sent", "channel": "WHATSAPP", "conversation_id": "01JPQ...", "responder": "HUMAN", "held": true }
Response fields
message_idstring | nullnull for web.statusstringsent, or queued when a web visitor is away: the message waits up to seven days and is shown when they return.channelstringconversation_idstringrecipient_onlinebooleanresponderstringHUMAN, under your API key's name.heldbooleantrue when this send put you in charge of the conversation. See Who is answering.Errors
| Status | When |
|---|---|
401 | The signature header is missing or does not match the body. |
400 | The channel is not connected to this chatbot. |
422 | The message is malformed, or window_closed on WhatsApp. |
404 | Web: no conversation with that id belongs to this chatbot. |
410 | Web: the visitor has been away for more than seven days. |
502 | The channel refused to deliver it. |
Who Is Answering
A message sent through this endpoint is treated like a person stepping in from the inbox, not as something the bot said.
- It is filed as a
HUMANmessage, signed withsender_nameor, without one, your API key's name asresponder_name. A web visitor sees a text reply under that name, as they would an inbox agent's. - If the bot was handling the conversation, your send takes it over: the conversation moves to
AGENT_ACTIVE,heldistrue, and the bot stays quiet. - You keep the conversation for 15 minutes after your last send or action. Every send extends it. When you go quiet, the customer's next message is answered by the bot again.
- A conversation an inbox agent already holds is not taken: your message is still delivered,
heldisfalse, and the agent keeps it. - Read what the customer writes back by polling Get Conversation Messages.
To hold a conversation before you have anything to say, or to hand it back the moment you are done, use the action below.
Hold or Release a Conversation
POST /api/dev/v1/chatbots/{chatbot_id}/conversations/{conversation_id}/action Authorization: Bearer <api-key> Content-Type: application/jsonPOST /api/dev/v1/chatbots/{chatbot_id}/conversations/{conversation_id}/action Authorization: Bearer <api-key> Content-Type: application/json
Request body
actionstringrequiredtakeover to hold the conversation so the bot stays quiet, or release to hand it back to the bot at once.
curl -X POST https://developers.sarufi.io/api/dev/v1/chatbots/<chatbot-id>/conversations/<conversation-id>/action \ -H "Authorization: Bearer <your-api-key>" \ -H "Content-Type: application/json" \ -d '{"action":"release"}'curl -X POST https://developers.sarufi.io/api/dev/v1/chatbots/<chatbot-id>/conversations/<conversation-id>/action \ -H "Authorization: Bearer <your-api-key>" \ -H "Content-Type: application/json" \ -d '{"action":"release"}'
Response - 200 OK
{ "status": "released" }{ "status": "released" }
takeover answers { "status": "held" }. A takeover on a conversation an inbox agent holds, or one that is closed, is refused with 409. A conversation id that is not this chatbot's answers 404.
Conversation Object
ConversationRecord fields
idstringchatbot_idstringuser_phonestring+255712345678 for WhatsApp).contact_namestring | nullchannelstringwhatsapp, web, sms, api.statusstringACTIVE, CLOSED, ARCHIVED, HANDOFF_PENDING, AGENT_ACTIVE.last_message_idstring | nulllast_message_atstringlast_message_previewstring | nullunread_countintegercreated_atstringupdated_atstring | nullMessage Object
MessageRecord fields
idstringconversation_idstringmessageobject | nullresponseobject | nullmessage_typestringtext, image, document).responderstringBOT or HUMAN.responder_namestring | nullHUMAN reply, who it was: the inbox agent's name, or the API key's name for a message sent through this API.received_atstringresponded_atstring | nullstatusstringcreated_atstringWant to chat with your bot via API?
Use the Chat API to create sessions and send messages programmatically. Conversations created via the Chat API appear here with channel=api.