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

json
{
  "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, webchat or simulator.
  • status: open, snoozed or resolved.
  • sla_due_at is set while the client waits for an answer: by then someone should reply.
  • do_not_contact is true when the client opted out on this channel.

List conversations

GET/v1/conversationsmessages:read
ParameterDescription
statusopen (default, includes snoozed ones whose time is up), snoozed, resolved or all
channelA channel id
lead, contactConversations of a lead or contact
unassigned, waiting, needs_reply, unreadtrue to keep only conversations without an assignee, past their answer time, waiting for an answer, or with unread messages
qSearch in the client's name, handle, phone and the last message
sortrecent (default) or sla (most urgent first)
limit, cursorSee Pagination
GET/v1/conversations/countsmessages:read

Counters for the inbox tabs: {"all": 12, "mine": 0, "unassigned": 4, "waiting": 2, "unread": 5}.

GET/v1/conversations/{id}messages:read

Messages

GET/v1/conversations/{id}/messagesmessages:read

Newest first, with cursor pagination.

json
{
  "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_at is set when the client (or, in Telegram Business chats, the account owner) edited the message in the messenger; text is the new version (message.edited).
  • deleted_at is set when the message was deleted in the messenger (Telegram Business chats). It stays in the history, marked (message.deleted).
  • external is true for an outgoing message written outside Kweko: in a telegram_business conversation, what the account owner sent from their own Telegram app. It has author_type member, no author_id and status sent.
  • In telegram_business conversations, 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 as failed with the reason.

Send a message

POST/v1/conversations/{id}/messagesmessages:send
FieldDescription
textRequired, up to 4096 characters
client_idOptional, 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.
forcetrue to send even though the client opted out (see below)
Request
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.

POST/v1/messages/{id}/retrymessages:send

Sends a failed outgoing message again (not_failed otherwise).

Manage conversations

POST/v1/conversations/{id}/assignconversations:manage

Body {"assignee_id": "member_…"}, or null to unassign.

POST/v1/conversations/{id}/resolveconversations:manage
POST/v1/conversations/{id}/reopenconversations:manage
POST/v1/conversations/{id}/snoozeconversations:manage

Body {"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.