Inbox
The Inbox holds two-way conversations with contacts. The Sent messages page is different: it is the delivery log of campaign and broadcast sends.
How messages arrive
| Channel | Source | Unknown sender |
|---|---|---|
| Telegram | Bot webhook, registered automatically when the bot token is saved (Settings → Telegram shows its health and can re-register it) | A contact is created with the Telegram chat as identity and attributes.createdFrom = "inbound:telegram" |
Meta webhook (/api/providers/whatsapp/webhooks) | A contact is created with the phone number and a WhatsApp identity | |
| SMS | Provider webhook (/api/providers/sms/webhooks), except STOP/START/HELP keywords | A contact is created with the phone number |
Every inbound message is added to the contact’s open conversation on that channel, or starts a new one. Duplicate webhook deliveries are ignored. Auto-reply and assignment rules run on each message, and the assignee (or the whole team for a new unassigned conversation) is notified.
Telegram specifics
/startrecords Telegram consent as opted in,/stoprecords it as opted out.- Each contact profile has a Copy Telegram link button. The link opens the bot with a signed start token. When the person presses Start, the chat is attached to that contact, and a placeholder contact created earlier for the same chat is merged into it.
- Telegram only lets a bot message people who have started it, so share the link before starting a Telegram conversation.
Working a conversation
- Reply sends through the conversation’s channel (Telegram, WhatsApp, SMS or email) and shows whether delivery succeeded. Failed deliveries can be retried. Email replies are transactional: they reach the contact even if they opted out of email. Telegram and SMS replies are not sent to a contact who opted out of that channel.
- Internal note is visible to teammates only.
- Assignee and status (Open, Waiting on contact, Resolved, Closed). Replying assigns an unassigned conversation to you and marks it as waiting on the contact.
- Link to contact moves the conversation and its channel identity to an existing contact.
- New conversation starts a thread with any contact and sends the first message.
List Threads
GET /api/workspaces/{workspaceId}/inbox
Paginated list of inbox threads.
Auth: Session required
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
page | number | 1 | Page number |
limit | number | 50 | Items per page |
status | string | — | OPEN, PENDING, RESOLVED, CLOSED, or active for open and pending |
channel | string | — | TELEGRAM, WHATSAPP, SMS, EMAIL, … |
assigned | string | — | me, unassigned or a user id |
contactId | string | — | Only conversations with this contact |
q | string | — | Search contact name, email, phone, subject and message text |
The response also includes counts with open, mine and unassigned.
Response:
{
"data": [
{
"id": "thr_abc123",
"channel": "EMAIL",
"subject": "Question about pricing",
"status": "OPEN",
"priority": "NORMAL",
"assignedTo": "usr_123",
"tags": ["sales", "pricing"],
"contact": {
"id": "ct_1",
"email": "jane@example.com",
"firstName": "Jane"
},
"lastReply": {
"body": "Hi, I have a question...",
"createdAt": "2026-01-15T10:00:00.000Z"
},
"createdAt": "2026-01-15T09:00:00.000Z"
}
],
"pagination": { "page": 1, "limit": 50, "total": 100, "totalPages": 2 }
}Create Thread
POST /api/workspaces/{workspaceId}/inbox
Auth: Session required
Request Body:
{
"contactId": "ct_abc123",
"channel": "EMAIL",
"subject": "Onboarding follow-up"
}Get Thread
GET /api/workspaces/{workspaceId}/inbox/{threadId}
Returns full thread details with all replies in chronological order.
Add Reply
POST /api/workspaces/{workspaceId}/inbox/{threadId}
Request Body:
{
"sender": "AGENT",
"body": "Thanks for reaching out! Here's the information you requested...",
"metadata": {}
}| Sender | Description |
|---|---|
AGENT | Reply from a team member |
CONTACT | Reply from the contact (triggers auto-reply processing) |
SYSTEM | System-generated message |
Update Thread
PATCH /api/workspaces/{workspaceId}/inbox/{threadId}
Update thread metadata.
Request Body:
{
"status": "RESOLVED",
"assignedTo": "usr_456",
"priority": "HIGH",
"tags": ["escalated", "billing"]
}Auto-Reply Rules
Automatically respond to contact messages based on conditions.
GET /api/workspaces/{workspaceId}/inbox/auto-replies
Lists all auto-reply rules ordered by priority.
POST /api/workspaces/{workspaceId}/inbox/auto-replies
Creates a new auto-reply rule.
PATCH /api/workspaces/{workspaceId}/inbox/auto-replies/{ruleId}
Updates an existing auto-reply rule.
DELETE /api/workspaces/{workspaceId}/inbox/auto-replies/{ruleId}
Deletes an auto-reply rule.
Assignment Rules
Automatically assign threads to team members based on conditions.
GET /api/workspaces/{workspaceId}/inbox/assignment-rules
Lists all assignment rules ordered by priority.
POST /api/workspaces/{workspaceId}/inbox/assignment-rules
Creates a new assignment rule.
PATCH /api/workspaces/{workspaceId}/inbox/assignment-rules/{ruleId}
Updates an existing assignment rule.
DELETE /api/workspaces/{workspaceId}/inbox/assignment-rules/{ruleId}
Deletes an assignment rule.
Canned Responses
Pre-written response templates for quick replies.
GET /api/workspaces/{workspaceId}/inbox/canned-responses
Lists all canned responses ordered by title.
POST /api/workspaces/{workspaceId}/inbox/canned-responses
Creates a new canned response.
PATCH /api/workspaces/{workspaceId}/inbox/canned-responses/{responseId}
Updates an existing canned response.
DELETE /api/workspaces/{workspaceId}/inbox/canned-responses/{responseId}
Deletes a canned response.
Delivery endpoints
POST /api/workspaces/{workspaceId}/inbox/{threadId}
Agent replies ("sender": "AGENT") are delivered through the thread channel; the returned reply has metadata.delivery with status (sent, failed, skipped), error and providerMessageId. Pass "metadata": { "internal": true } for an internal note.
POST /api/workspaces/{workspaceId}/inbox/{threadId}/replies/{replyId}/deliver
Retries delivery of an agent or auto-reply message that was not delivered.
PATCH /api/workspaces/{workspaceId}/inbox/{threadId}
Besides status, assignedTo, priority and tags, accepts contactId to move the conversation (and its Telegram, WhatsApp or phone identity) to another contact.
GET /api/workspaces/{workspaceId}/contacts/{contactId}/telegram-link
Returns { url, linked }, the personal https://t.me/<bot>?start=… link for the contact.
Commands and quick replies
Telegram, WhatsApp and SMS share one command list, in Settings under each channel. A command has a keyword, a description, a reply, and a reply per language.
| Channel | How a contact triggers it | Menu |
|---|---|---|
| Telegram | /promo | Published to Telegram’s command menu, one menu per language |
PROMO or /promo | No menu | |
| SMS | PROMO (plus the usual STOP, START, HELP, and LANGUAGE keywords) | No menu |
Built-in commands are start, stop, help and language; start and stop also record channel consent. help renders {{commands}}, the enabled commands with their descriptions in the contact’s language.
Languages
Each command keeps a default reply plus a reply per language. A contact receives the reply for their locale, falling back to the language without the region, then another regional variant, then the workspace default, then the default reply. Descriptions are localized the same way, and Telegram menus are published per language.
/language lets a contact set their own language: /language pt, /language pt-BR, or LANGUAGE pt on WhatsApp and SMS. It stores the locale and country on the contact and confirms in the new language, so every later email, Telegram message, push and SMS uses it. Sending it with no code replies with the list of languages you reply in.
Replies support contact tokens, {{args}}, {{commands}}, {{languages}} and promo codes such as {{contact.id | code:PROMO10}}.
Every command emits telegram.command, whatsapp.command or sms.command with command and args in metadata, so a campaign with an Event trigger can react to it.
API
GET /api/workspaces/{workspaceId}/channel-commands?channel=TELEGRAM returns the commands, and PUT the same path with { channel, commands: [{ command, description, reply, translations, descriptions, enabled, showInMenu }] } saves them and republishes the Telegram menu.
Real-time automations
Email opens and clicks (message.open, message.click), and inbound telegram.message_received, telegram.command, whatsapp.message_received and sms.message_received events run automations immediately:
- Campaigns with an Event trigger for that event enroll the contact.
- Enrollments waiting on a Decision node for that contact are re-evaluated at once, so “opened the email → send a Telegram message” happens seconds after the open instead of at the next scheduled check.
The code template filter produces a stable, contact-specific code: {{contact.id | code:OPEN15}} renders the same OPEN15-XXXXXX value in every email and Telegram message for that contact.