Sending messages: endpoint reference
A full endpoint reference for external systems (AI assistants, chatbots, CRMs): channels, media and voice uploads, sending text and WhatsApp templates.
This reference complements the "Sending messages from your system" walkthrough with per-endpoint detail: request fields, response shape, and edge cases. Every request uses the company's Bearer token and runs in that company's context.
GET /channels
Fetch the channels available to the current user within the company — use it to find the channel_id you'll send through.
| Query param | Description |
|---|---|
type | Filter by channel type key: whatsapp, whatsapp_business, telegram, telegram_bot, instagram, instagram_business, facebook_messenger, tiktok, live_chat, sms |
status | Filter by status: connected, disconnected, paused, etc. |
setup_state | Filter by setup state: Draft, Setup, Active, Paused |
{
"data": [
{
"id": 57,
"name": "WhatsApp test",
"account_name": "+77084252291",
"status": "connected",
"message_operations": {
"can_create": true,
"can_edit": true,
"edit_window_seconds": 900,
"revoke_window_seconds": 172800
},
"limits": {
"max_text_length": 4096,
"file_limits": { "image": { "max_size_mb": 16 }, "video": { "max_size_mb": 64 } }
},
"channel_type": { "id": 1, "key": "whatsapp", "name": "WhatsApp" },
"broadcast_rate_limits": { "daily_limit": 1500, "messages_per_minute": 15 }
}
]
}Before sending, check that status === "connected" and message_operations.can_create === true.
POST /uploads
Uploads a file to AISAR and returns a compact media_id you then pass to /messages/send. Delivery to the messenger is handled by the connector without re-downloading. Two modes are supported.
Mode A — multipart upload
POST /v1/uploads
Authorization: Bearer YOUR_API_TOKEN
Content-Type: multipart/form-data
file=@photo.jpgMaximum 64 MB. Allowed extensions: jpg, jpeg, png, gif, webp, bmp, svg, mp4, mov, avi, wmv, webm, mkv, mp3, ogg, wav, aac, flac, m4a, opus, amr, pdf, doc, docx, xls, xlsx, csv, txt, rtf, ppt, pptx, zip, rar, 7z, gz, json, xml.
Mode B — upload by URL
POST /v1/uploads
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
{ "url": "https://cdn.example.com/photo.jpg" }AISAR downloads the file once, stores it, and returns a media_id. The URL must be http/https, the size ≤ 64 MB, download timeout 20 sec.
{
"media_id": "med_01HZABCDEFGHIJKLMNOPQRSTUV",
"type": "image",
"filename": "photo.jpg",
"mime_type": "image/jpeg",
"size": 123456,
"width": null,
"height": null,
"duration": null,
"expires_at": "2026-05-16T10:30:00.000000Z"
}media_id; the file isn't duplicated in storage. A broadcast of one image to 100 contacts needs only one POST /uploads, even if you accidentally call it twice.Requires the conversation.view permission.
POST /uploads/voice
Uploads an audio recording and transcodes it to OGG/Opus — the format WhatsApp and Telegram render as a voice note rather than an attached file. This is the only correct way to send a voice message via the API: a plain /uploads call stores the file as-is (type audio), and the recipient gets an audio file, not a voice note.
POST /v1/uploads/voice
Authorization: Bearer YOUR_API_TOKEN
Content-Type: multipart/form-data
file=@voice.m4aAccepts audio/webm, audio/ogg, audio/mp4, audio/mpeg, audio/wav, audio/aac, audio/x-m4a (and video/webm — the browser-recording container). The server transcodes the recording to OGG/Opus itself.
{
"media_id": "med_01HZABCDEFGHIJKLMNOPQRSTUV",
"type": "audio",
"filename": "voice.ogg",
"mime_type": "audio/ogg",
"size": 24576,
"duration": 7,
"expires_at": "2026-05-16T10:30:00.000000Z"
}type field in the response is audio (it's a media asset). To make the message send as a voice note, set attachments[].type: "voice" when sending (see below). Voice notes are supported on WhatsApp (personal), WhatsApp Business, Telegram (personal), and Telegram Bot; on other channels the attachment goes out as a regular audio file. Requires the conversation.view permission.POST /messages/send
Sends an outbound message on behalf of a channel to a specific recipient. If the conversation doesn't exist yet, it will be created.
| Field | Type | Required | Description |
|---|---|---|---|
channel_id | integer | Yes | The sending channel ID (from /channels) |
recipient | string | Yes | The recipient identifier in the format the channel expects: an E.164 number, a Telegram username, an IGSID, etc. For WhatsApp groups — the group JID <id>@g.us. Max 255 chars. |
is_group | boolean | No | true — the recipient is a WhatsApp group. Not required if recipient is already in the <id>@g.us form (the suffix is recognized automatically). Defaults to false. |
is_silent | boolean | No | true — a "silent" send: the operator's unread counter is not reset. Defaults to false. Useful for programmatic/service messages. |
message_content | string | Conditional | Message text, max 65,535 chars (the channel's actual limit is in limits.max_text_length from /channels). Required if attachments is absent. |
attachments | array | Conditional | 1–10 attachments. Required if message_content is absent. Each item: media_id (required) + optional type (image/video/audio/voice/document/file). |
{
"channel_id": 42,
"recipient": "+79001234567",
"message_content": "Здравствуйте! Чем могу помочь?"
}{
"channel_id": 42,
"recipient": "120363398017778174@g.us",
"message_content": "Всем привет в группе!"
}Where to get the group identifier: the JID <id>@g.us arrives in inbound-message webhooks and is available in GET /v1/conversations — the group thread's threads[].contact_account.external_id (threads[].is_group: true). Constraints: groups are only supported on WhatsApp channels; the channel's account must be a member of the group; a contradictory request (recipient ends with @g.us but is_group: false) is rejected with 422.
{ "success": true }Behaviour. If a conversation with the recipient already exists on this channel it is reused; otherwise a new one is created (contact account, thread, conversation). After the message is created, subscribed webhook integrations receive message.created with direction: outbound. The endpoint creates the message synchronously, but delivery to the messenger runs asynchronously through the connector queue — the final delivery status arrives as message.status.updated. Requires the conversation.create permission, otherwise 403 Forbidden.
GET /templates
Fetches the company's message templates — use it to find Meta-approved WhatsApp Business templates and their id for /messages/send-template.
| Query param | Description |
|---|---|
channel_id | Filter by a specific WhatsApp Business channel |
metaStatuses[] | Meta approval status: approved, rejected, pending. For sendable templates use metaStatuses[]=approved |
channelType | Channel type key (e.g. whatsapp_business) |
language | BCP-47, e.g. en, ru |
search | Search by name/text/slug/labels |
sortBy / sortDir | name, status, created_at, updated_at, uses_count, language; asc/desc |
{
"data": {
"items": [
{
"id": 42,
"name": "order_update",
"meta_status": "approved",
"language": "en",
"body": "Здравствуйте, {{name}}! Ваш заказ {{order_id}} готов.",
"parameters": [
{ "name": "name", "type": "text", "sample_value": "Иван", "position": 1 },
{ "name": "order_id", "type": "text", "sample_value": "A-100", "position": 2 }
],
"channel_type": { "id": 1, "key": "whatsapp_business", "name": "WhatsApp Business" },
"channel": { "id": 57, "name": "WhatsApp test", "account_name": "+77084252291" }
}
],
"pagination": { "page": 1, "perPage": 15, "total": 1, "lastPage": 1 }
}
}meta_status is the Meta review status; only `approved` can be sent. body carries the placeholders ({{name}} or {{1}}) that tell you which params /messages/send-template needs. Requires the template.view permission.
template.approved and template.rejected webhook events — they fire when meta_status changes, letting you invalidate a cache.POST /messages/send-template
Sends an approved WhatsApp Business template to a specific recipient — for initiating conversations outside the 24-hour window and for broadcasts (call once per recipient). If a conversation with the recipient doesn't exist yet, it will be created.
| Field | Type | Required | Description |
|---|---|---|---|
channel_id | integer | Yes | Must be of type whatsapp_business |
recipient | string | Yes | E.164 number, max 255 chars |
template_id | integer | Yes | Template ID from /templates, must belong to the company and have meta_status = approved |
params | object/array | No | Template variable values (see below). Omit for templates without variables. |
header_media_url | string (url) | No | For templates with a media header (image/video/document) |
is_silent | boolean | No | Same as /messages/send — does not reset the unread counter |
The server builds the Meta components structure from the template definition for you. params values are passed one of two ways: a named map for named placeholders {{name}} ({ "name": "Иван", "order_id": "A-100" }), or positional for numeric placeholders {{1}}, {{2}} — a map with numeric keys or a plain array (["Иван", "A-100"], values in order: header → body → buttons).
{
"channel_id": 57,
"recipient": "+79001234567",
"template_id": 42,
"params": { "name": "Иван", "order_id": "A-100" }
}{
"success": true,
"message_id": 12345
}Behaviour. Sending is only allowed for whatsapp_business channels and templates with meta_status = approved — otherwise 422. If a variable's value is missing, 422 lists the missing parameters. The sent message keeps the rendered template structure in message_content.display (visible in GET /messages): header, body, footer, buttons[] — each button is { "type": "quick_reply" | "url" | "phone", "label": "...", "value": "..." }. When a contact taps a quick-reply button, the tap arrives back as an inbound message of type: "text" carrying the button's label and content.interactive_reply_payload. Requires the conversation.create permission.
Errors and status codes
| Code | Cause | How to handle |
|---|---|---|
401 | Invalid or missing token | Check Authorization: Bearer {token} |
403 | No permission to create conversations | The token owner needs the conversation.create permission |
404 | Company not found | The token isn't bound to a company |
422 | Validation error or the channel doesn't belong to the company | Check the request body |
429 | Rate limit | Retry with exponential backoff |
The full request → webhook loop and media-broadcast walkthrough live in the "Sending messages from your system" guide.