Send messages to your contacts on any channel β WhatsApp Official, WhatsApp Unofficial, Instagram, Facebook, Telegram, and Webchat β through one channel-agnostic endpoint:
Code
You supply a channel (which number/inbox to send from) and a recipient β ChatMo resolves or creates the conversation for you, so you never have to manage conversation IDs.
:::info Authentication
Every request needs your API key in the x-api-key header (or a Bearer JWT). API keys are available on the Professional plan and above β create one in the dashboard under Settings β Developers β API Keys.
:::
Quick start
Code
A successful call returns 200 with the created message:
Code
Step 1 β Get your channel_id
channel_id tells ChatMo which of your connected numbers/accounts to send from (one organization can have many). You have two ways to find it.
Option A β Look it up by a memorable identifier
Use GET /channels/by-identifier with something you already know β a phone number, an @username, a Page name, or a widget id:
Code
Code
Which identifier matches depends on the channel type:
| Channel | Identifier to pass |
|---|---|
| WHATSAPP_OFFICIAL | phone number, e.g. 628123456789 |
| WHATSAPP_UNOFFICIAL | device phone number |
@username | |
| Page name | |
| TELEGRAM | bot @username or numeric bot id |
| WEBCHAT | widget id |
Phone numbers are matched digit-normalized, so +62β¦, 62β¦, and spaced/dashed variants all resolve. Add &type=WHATSAPP_OFFICIAL to disambiguate when the same identifier could match more than one channel.
Option B β List all your channels
GET /inboxes returns a paginated list of your channels (id, name, type, status) β handy for building a dropdown.
Code
Step 2 β Address the recipient
Provide a to object alongside channel_id. For WhatsApp, pass the full international phone number with the country code included and no leading 0 (e.g. 628123456789) β don't send a separate country code.
Code
name is optional and used only when the contact is brand new.
What to put in to, per channel:
| Channel | to |
|---|---|
| WhatsApp Official / Unofficial | { "phone": "628123456789" } |
{ "type": "external_id", "value": "<Instagram-scoped user id>" } | |
{ "type": "external_id", "value": "<Page-scoped PSID>" } | |
| Telegram | { "type": "external_id", "value": "<Telegram user id>" } |
| Webchat | reply via conversation_id (webchat visitors have no dialable address) |
For Instagram / Facebook / Telegram, to.value is the platform-scoped user id captured on inbound webhooks β it's stored as the contact's external_id.
:::tip Easiest path β reply in an existing thread
On any channel you can skip to and pass conversation_id to reply inside a known conversation:
Code
:::
Per-channel examples:
Code
Message types
type selects the message kind. Support varies by channel:
| Type | WA Official | WA Unofficial | Telegram | Webchat | ||
|---|---|---|---|---|---|---|
| TEXT | Yes | Yes | Yes | Yes | Yes | Yes |
| IMAGE / VIDEO / DOCUMENT / AUDIO / FILE | Yes | Yes | Yes | Yes | Yes | Yes |
| TEMPLATE | Yes | β | β | β | β | β |
| INTERACTIVE | Yes (native, β€3) | As numbered text | Yes (native, β€13) | β | β | β |
A dash (β) means the type is not supported on that channel β sending it returns 400 UNSUPPORTED_MESSAGE_TYPE. FILE is an alias for DOCUMENT.
Text
Code
Image / Video / Audio / Document
Media accepts either a file_id (uploaded via POST /uploads, see below) or a public url:
Code
Code
The media's real type must match type β e.g. sending a .pdf as IMAGE is rejected with 400 VALIDATION_ERROR. DOCUMENT accepts any file.
Template (WhatsApp Official only)
Templates are the only way to start a WhatsApp conversation outside the 24-hour customer-service window. The approved template is re-resolved server-side by (name + language + channel); you only supply the variable values.
Code
variablesare keyed"1","2", β¦ matching the{{1}},{{2}}placeholders in the template body.media_urlis only needed when the template's header is an image/video/document.- Find your approved templates (name, language, variables) via
GET /whatsapp-templates.
:::tip Finding templates
Code
:::
Interactive (reply buttons)
Native reply buttons on WhatsApp Official (max 3) and Instagram (max 13). On WhatsApp Unofficial they degrade to a numbered-text list; other channels return 400.
Code
The id is the payload echoed back to you when the customer taps the button.
Uploading media
To send media by file_id, upload the file first with POST /uploads (multipart):
Code
The response returns an id β pass it as media.file_id. Alternatively, skip the upload entirely and send a public media.url.
Optional fields
| Field | Description |
|---|---|
reply_to_message_id | Native quoted reply β the internal message UUID or the channel-side external id of the parent message. |
client_reference | Your own correlation id (β€128 chars). Echoed back in the response and stored on the message so you can reconcile later. |
Error handling
Errors return a success: false envelope. The human-readable reason is in message; a stable machine code is in data.error_code β switch on that in your integration.
Code
| HTTP | error_code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | Missing/invalid field, or media type mismatch. |
| 400 | UNSUPPORTED_MESSAGE_TYPE | The type isn't supported on that channel. |
| 401 | β | Missing or invalid API key. |
| 402 | SUBSCRIPTION_EXPIRED | Renew your subscription to keep using the API. |
| 403 | FORBIDDEN | Your key/role lacks permission. |
| 404 | CHANNEL_NOT_FOUND | Unknown channel or not in your organization. |
| 404 | CONVERSATION_NOT_FOUND | Unknown conversation_id. |
| 404 | TEMPLATE_NOT_FOUND | No approved template for that name/language/channel. |
| 404 | MEDIA_NOT_FOUND | file_id not found or not finished processing. |
| 422 | CHANNEL_INACTIVE | The channel isn't active. |
| 502 | PROVIDER_ERROR | The channel provider rejected the message (detail in data.detail). The message is still recorded as FAILED. |
| 500 | INTERNAL_ERROR | Unexpected server error β retry. |
:::info Tracking sent messages Every call is logged in the dashboard under Settings β Developers β Send Message by API β with the request, response, HTTP status, latency, and delivery status. Messages sent through the API also show a "via API" badge in the chat inbox. :::
Related endpoints
| Endpoint | Purpose |
|---|---|
GET /channels/by-identifier | Resolve a channel_id from a phone/username/page/widget id. |
GET /inboxes | List all your channels. |
GET /whatsapp-templates | List approved WhatsApp templates (name, language, variables). |
POST /uploads | Upload media and get a file_id. |
GET /conversations/{conversation_id}/messages | Read a conversation's message history. |
