Chatqus CRM API

Messages

Server

Message retrieval and sending


List messages in a conversation

GET
https://api-chat.misindo.id
/api/conversations/{id}/messages

Cursor-paginated. Returns newest first.

List messages in a conversation › path Parameters

id
​string · uuid · required

List messages in a conversation › query Parameters

cursor
​string

MongoDB ObjectId hex string for pagination

limit
​integer · min: 1 · max: 100
Default: 50

List messages in a conversation › Responses

200

Paginated messages

​Message[] · required
has_more
​boolean · required
next_cursor
​string

MongoDB ObjectId hex for next page


Send an outbound message

POST
https://api-chat.misindo.id
/api/conversations/{id}/messages

Produces an outbound job to Kafka. The message-sender service delivers it via Meta Graph API (WhatsApp/Instagram) or SMTP (Email).

type: flow sends a WhatsApp Flow directly (requires wa_flows.send, 403 otherwise; content follows FlowSendContent). When the template carries a FLOW button the caller additionally needs wa_flows.send (403).

Send an outbound message › path Parameters

id
​string · uuid · required

Send an outbound message › Request Body

type
​MessageType · enum · required

Mirrors MessageType in crates/omni-common/src/models/message.rs. contacts (a shared contact card) and postback (a tapped button) are emitted by the channel engines; unknown covers anything the parsers did not recognise. flow is an outbound WhatsApp Flow send (content follows FlowSendContent, requires wa_flows.send); flow_response is the matching inbound completion and is never sent by a caller — its content carries a masked summary only, never the customer's answers (see WhatsAppFlowCompletedEvent and GET /api/wa-flow-responses/{session_id} for the real values).

Enum values:
text
image
document
template
audio
video
location
sticker
content
​required

Free-form JSON. For text: {"text": "Hello"} or {"body": "Hello"} — the WhatsApp sender reads text first and falls back to body. For image: {"url": "...", "caption": "..."}. Whichever key you send is stored verbatim and read back on Message.content, so sending body keeps writes and reads on the same key that every inbound message already uses.

Send an outbound message › Responses

200

Message created and queued for delivery

A stored message, as returned by `GET /api/conversations/{id}/messages` and by `POST /api/conversations/{id}/messages`. Mirrors `Message` in `crates/omni-common/src/models/message.rs`.
conversation_id
​string · uuid · required
direction
​MessageDirection · enum · required
Enum values:
inbound
outbound
type
​MessageType · enum · required

Mirrors MessageType in crates/omni-common/src/models/message.rs. contacts (a shared contact card) and postback (a tapped button) are emitted by the channel engines; unknown covers anything the parsers did not recognise. flow is an outbound WhatsApp Flow send (content follows FlowSendContent, requires wa_flows.send); flow_response is the matching inbound completion and is never sent by a caller — its content carries a masked summary only, never the customer's answers (see WhatsAppFlowCompletedEvent and GET /api/wa-flow-responses/{session_id} for the real values).

Enum values:
text
image
document
template
audio
video
location
sticker
content
​required

Free-form JSON whose shape depends on type. READING a stored message: text lives under body. Every channel engine normalises inbound content to {"body": "..."} before storing it (see the parser modules in crates/chat-engine), and outbound text stored through this API arrives the same way from the first-party clients. The server's own reader, extract_content_text in crates/api-gateway/src/routes/messages/handlers.rs, tries body, then text, then caption, then subject — a consumer should do the same rather than reading one key. Email messages carry body, subject, html and text together. WRITING via SendMessageRequest: either key works. The WhatsApp sender reads text first and falls back to body (crates/message-sender/src/whatsapp.rs), and whichever you send is stored verbatim — so send body if you want reads and writes to agree.

status
​MessageStatus · enum · required
Enum values:
pending
sent
delivered
read
failed
created_at
​string · date-time · required
​object

MongoDB ObjectId in extended JSON — {"$oid": "<24-hex>"}, NOT a bare string. The server field is a bson::oid::ObjectId, which serializes to this wrapper object under serde_json. Note the asymmetry with MessageSearchResult._id, which IS a plain hex string: the search handler flattens it with oid.to_hex() while building the result. The same logical value therefore arrives in two different shapes depending on the endpoint. MessageListResponse.next_cursor and the cursor query parameter both use the plain hex form, so the value read from here must be unwrapped before it can be used as a cursor. Absent on a message whose id was never assigned (the field is skipped when null).

external_id
​string

The provider's own id for this message — a WhatsApp wamid.* from the Cloud API, a Meta message id for Instagram/Messenger, or the SMTP Message-ID for email. Distinct from _id, which is Omnistream's id and the one every REST endpoint takes and returns. Outbound messages are stored before the provider has assigned one, so this is null between the send call returning and the provider accepting the message (typically under a second). It is populated in place once message-sender gets the provider's response, and it is the key delivery receipts arrive under — see WebhookMessagePreview for how to correlate the two ids.

sender_phone
​string
sent_by_agent_id
​string · uuid

Agent who sent this outbound message. Absent on inbound and system-generated messages.

sent_by_agent_name
​string

Denormalized display name of the sending agent

​MessageReaction[]

CRM-internal agent reactions. Never forwarded to the external channel. Absent when nobody has reacted.

error_message
​string

Why delivery failed — human-readable, only set when status is failed

error_code
​string

Provider error code for the failure (e.g. "132001" from Meta)

error_source
​string

Origin of the failure ("meta", "smtp", or "system")

error_details
​object

Raw provider error object (sanitized), for support/debugging


Send a WhatsApp template message in one call

POST
https://api-chat.misindo.id
/api/messages/template

Send a business-initiated WhatsApp template to a phone number without an existing conversation_id. Auto-creates the contact and conversation, sends the template, and logs it under a campaign (shown as "Sent via API") with delivery tracking. Intended for third-party integrations.

Authenticate with X-API-Key (recommended) or a bearer JWT. Requires the campaigns.broadcast permission.

When the template carries a FLOW button the caller additionally needs wa_flows.send (403).

Send a WhatsApp template message in one call › Request Body

to
​string · required

Destination phone in international format, no +.

template
​string · required

Approved template name as registered with Meta.

language
​string

Template language code. Optional if the name is unambiguous.

integration_account_id
​string · uuid

WhatsApp account to send from. Defaults to the active default.

campaign_name
​string

Label shown in the broadcast history. Defaults to "API:

body_params
​string[]

Positional values for body variables {{1}}..{{N}}.

button_url_param
​string

Value substituted into a dynamic URL button's {{1}}.

​object[]

Escape hatch — raw Meta Cloud API components array. When present it is used verbatim and body_params/button_url_param are ignored.

Send a WhatsApp template message in one call › Responses

Template accepted and queued for delivery.

message_id
​string

MongoDB message id (hex).

conversation_id
​string · uuid
campaign_id
​string · uuid

Campaign this send is logged under.

status
​string

Search messages across conversations

GET
https://api-chat.misindo.id
/api/messages/search

Search messages across conversations › query Parameters

q
​string · required

Search query (text content). Trimmed; a blank query returns an empty page.

conversation_id
​string · uuid

Scope the search to a single conversation

channel
​string · enum
Enum values:
whatsapp
instagram
email
messenger
direction
​MessageDirection · enum
Enum values:
inbound
outbound
from
​string · date-time

Start of a created-at range filter

to
​string · date-time

End of a created-at range filter

page
​integer · min: 1
Default: 1
per_page
​integer · min: 1 · max: 50
Default: 20

Search messages across conversations › Responses

200

Paginated search hits. Note this endpoint returns an envelope whose items are MessageSearchResult, NOT a bare array of Message.

​MessageSearchResult[] · required
total
​integer · int64 · required
page
​integer · int64 · required
per_page
​integer · int64 · required

Page size actually applied, clamped to 50

total_pages
​integer · int64 · required