API reference
Conversations and messages
The inbox: one conversation per client per channel (Telegram, WhatsApp, Instagram, web chat and others), with its messages. Send replies through the API and Kweko delivers them through the right channel.
Messages contain personal data. Reading them needs messages:read, which the read-only key preset leaves out on purpose.
The conversation object
{
"id": "conv_01j8z8d5e7f9g1h3j5k7m9n1p3",
"channel": { "id": "channel_01j8z5v3w5x7y9z1a3b5c7d9e1", "type": "telegram_bot", "name": "@acme_bot", "status": "active" },
"name": "Dilnoza Karimova",
"handle": "@dilnoza_k",
"phone": "+998901234567",
"contact_id": "contact_01j8z7c1a3b5c7d9e1f3g5h7j9",
"lead_id": "lead_01j8z7c2d4f6g8h0j2k4m6n8p0",
"status": "open",
"snoozed_until": null,
"assignee_id": "member_01j8z5x7y9z1a3b5c7d9e1f3g5",
"do_not_contact": false,
"last_message_at": "2026-09-27T09:41:12.482913Z",
"last_inbound_at": "2026-09-27T09:41:12.482913Z",
"last_outbound_at": "2026-09-27T09:12:00Z",
"unread_count": 1,
"sla_due_at": "2026-09-27T09:56:12Z",
"preview": { "text": "Is it available in blue?", "direction": "in" },
"created_at": "2026-09-27T09:12:00Z"
}channel.type:telegram_bot,telegram_business,telegram_account,whatsapp,instagram,email,webchatorsimulator.status:open,snoozedorresolved.sla_due_atis set while the client waits for an answer: by then someone should reply.do_not_contactistruewhen the client opted out on this channel.
List conversations
/v1/conversationsmessages:read| Parameter | Description |
|---|---|
status | open (default, includes snoozed ones whose time is up), snoozed, resolved or all |
channel | A channel id |
lead, contact | Conversations of a lead or contact |
unassigned, waiting, needs_reply, unread | true to keep only conversations without an assignee, past their answer time, waiting for an answer, or with unread messages |
q | Search in the client's name, handle, phone and the last message |
sort | recent (default) or sla (most urgent first) |
limit, cursor | See Pagination |
/v1/conversations/countsmessages:readCounters for the inbox tabs: {"all": 12, "mine": 0, "unassigned": 4, "waiting": 2, "unread": 5}.
/v1/conversations/{id}messages:readMessages
/v1/conversations/{id}/messagesmessages:readNewest first, with cursor pagination.
{
"id": "msg_01j8z8e6f8g0h2j4k6m8n0p2q4",
"conversation_id": "conv_01j8z8d5e7f9g1h3j5k7m9n1p3",
"direction": "in",
"author_type": "client",
"author_id": null,
"text": "Is it available in blue?",
"attachments": [],
"status": "received",
"error": null,
"client_id": null,
"created_at": "2026-09-27T09:41:12.482913Z",
"edited_at": null,
"deleted_at": null,
"external": false
}Outgoing messages go through status queued, sent, delivered and read as the channel reports them, or end as failed with a reason in error. Each change sends a message.<status> webhook.
edited_atis set when the client (or, in Telegram Business chats, the account owner) edited the message in the messenger;textis the new version (message.edited).deleted_atis set when the message was deleted in the messenger (Telegram Business chats). It stays in the history, marked (message.deleted).externalistruefor an outgoing message written outside Kweko: in atelegram_businessconversation, what the account owner sent from their own Telegram app. It hasauthor_typemember, noauthor_idand statussent.- In
telegram_businessconversations, replies are sent on behalf of the owner's account, so the client sees them from the owner, not from a bot. Telegram only allows them within 24 hours of the client's last message and when the owner allowed the bot to reply; otherwise the message ends asfailedwith the reason.
Send a message
/v1/conversations/{id}/messagesmessages:send| Field | Description |
|---|---|
text | Required, up to 4096 characters |
client_id | Optional, up to 64 characters: your id for this message. Sending again with the same client_id returns the already queued message instead of sending twice, so retries are safe. |
force | true to send even though the client opted out (see below) |
curl https://api.kweko.uz/v1/conversations/conv_01j8z8d5e7f9g1h3j5k7m9n1p3/messages \
-H "Authorization: Bearer $KWEKO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Yes, blue is in stock. Shall we deliver tomorrow?", "client_id": "order-1042-reply-1"}'Answers 201 with the message in status queued; delivery happens in the background, so watch message.sent or message.failed. Sending to a client who opted out answers 409 opted_out: only resend with "force": true if the client asked you to write. Channels have their own rules, for example WhatsApp only accepts free-form messages within 24 hours of the client's last message; such failures arrive as message.failed with the reason.
/v1/messages/{id}/retrymessages:sendSends a failed outgoing message again (not_failed otherwise).
Manage conversations
/v1/conversations/{id}/assignconversations:manageBody {"assignee_id": "member_…"}, or null to unassign.
/v1/conversations/{id}/resolveconversations:manage/v1/conversations/{id}/reopenconversations:manage/v1/conversations/{id}/snoozeconversations:manageBody {"until": "2026-09-28T09:00:00+05:00"}, within the next 90 days. The conversation reopens by itself at that time.
These also need messages:read to see the conversation. Assigning sends conversation.assigned; resolving, reopening and snoozing send conversation.status_changed.