Send Messages API for WhatsApp, Instagram and More
Send WhatsApp, Instagram, Messenger, Telegram, SMS, email and web chat messages with one REST endpoint, then check each message's delivery status by id.
Overview
The Send Messages API sends a business message to a contact on any channel your workspace has connected, using one endpoint: POST /api/v2/messages/send/. The same request shape works for WhatsApp, Instagram, Facebook Messenger, Telegram, SMS, email and web chat. You pick the channel with account_id, and ChatMaxima delivers the message through that channel and records it in your Inbox, tagged API.
Use it to send order updates, reply to a customer from your own system, or automate follow-ups on Instagram and WhatsApp without building a separate integration for each channel.
Prerequisites
- An API key on a plan with API access (Pro or higher). See ChatMaxima API.
- A connected channel account. Get its
account_idfrom Channel Accounts. - A contact on that account, or a phone number or email address for channels that accept one (see Choosing the recipient).
Send a message
curl -X POST https://chatmaxima.com/api/v2/messages/send/ \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"account_id": "a1b2c3d4e5f6",
"contact_id": "ct_abc123",
"text": "Hi Jane, your order #1001 has shipped.",
"message_ref_id": "order-1001-shipped"
}'
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
account_id | string | Yes | The channel account to send from, from Channel Accounts. |
contact_id | string | One of three | ChatMaxima's contact id, as returned by the Contacts API. |
contact_ref_id | string | One of three | Your own id for the contact (contact_vendor_ref_id in the Contacts API). |
to | string | One of three | A phone number (WhatsApp Cloud API and SMS) or an email address (email channels). |
text | string | Yes, unless media_url is set | Message text, up to 4,096 characters. Always required on email. |
media_url | string | No | An https:// URL of an image, video, audio file or document. |
media_type | string | With media_url | image, video, audio or file. |
file_name | string | No | File name shown to the recipient. Defaults to the name in the URL. |
subject | string | Email only | Email subject, up to 250 characters. Required on email channels. |
conversation_id | string | No | Web chat only. Continue a specific conversation instead of the contact's latest one. |
message_ref_id | string | No | Your idempotency key, up to 100 bytes. See Safe retries. |
Response
A successful send returns 201:
{
"status": "success",
"code": 201,
"message": "Message sent",
"data": {
"message_id": "123456",
"provider_message_id": "wamid.XXXX",
"account_id": "a1b2c3d4e5f6",
"channel": "fb_whatsapp",
"contact_id": "ct_abc123",
"conversation_id": "conv_abc123",
"direction": "outbound",
"status": "sent",
"error": null,
"sent_at": "2026-11-03T10:15:00Z",
"message_ref_id": "order-1001-shipped",
"duplicate": false
}
}
Keep message_id to check delivery later. provider_message_id is the id the channel itself assigned (for example a WhatsApp wamid), or null when the channel returns none. sent_at is in UTC.
Supported channels
| Channel | 24-hour window applies | to accepted | Notes |
|---|---|---|---|
| WhatsApp (Cloud API) | Yes | Phone number | Media can be any public https URL. |
| WhatsApp (Twilio) | Yes | No | |
| Yes | No | Reply to people who messaged your Instagram account. | |
| Facebook Messenger | Yes | No | |
| Telegram | No | No | |
| SMS | No | Phone number | Unknown numbers are added as new contacts. |
| Email and Tickets | No | Email address | subject and text are required. Each send starts a new thread. |
| Web chat | No | No | Use conversation_id to target a specific chat. |
| GoHighLevel | No | No |
Other account types, such as voice channels, return 422 CHANNEL_NOT_SUPPORTED.
The 24-hour messaging window
WhatsApp, Instagram and Messenger only accept a free-form business message within 24 hours of the contact's last message to you. ChatMaxima checks this before sending. If the window is closed, or the contact never messaged you, the request fails with 422 OUTSIDE_MESSAGING_WINDOW, and details.hours_since_last_inbound tells you how long it has been.
To start or reopen a WhatsApp conversation, send an approved template with the WhatsApp Templates API. On Instagram and Messenger, wait for the customer to write to you first.
Choosing the recipient
Send exactly one way of identifying the contact. contact_id and contact_ref_id are checked first, then to.
contact_idorcontact_ref_idmust belong to a contact who exists on the channel account you are sending from. Otherwise you get404 CONTACT_NOT_ON_CHANNEL.toon WhatsApp must be a number that has already messaged that WhatsApp account. A brand-new number returns422 OUTSIDE_MESSAGING_WINDOW. Use a template instead.toon SMS or email creates the contact if it does not exist yet, with source API.- Phone numbers can be sent with or without the leading
+, and must have 6 to 15 digits.
Sending media
Add media_url and media_type to send an image, video, audio file or document. text becomes the caption and can be left out.
- WhatsApp (Cloud API): any public
httpsURL works. WhatsApp fetches the file itself. - Every other channel: first upload the file with the Media Upload API, then pass the
media_urlit returns. URLs from anywhere else return422 MEDIA_URL_NOT_ALLOWED. - Files must be smaller than 10 MB.
{
"account_id": "a1b2c3d4e5f6",
"contact_id": "ct_abc123",
"text": "Here is your invoice.",
"media_url": "https://cdn.example.com/invoice-1001.pdf",
"media_type": "file",
"file_name": "invoice-1001.pdf"
}
Safe retries with message_ref_id
Set message_ref_id to an id from your own system, such as an order or event id. ChatMaxima remembers it for 24 hours within your workspace:
- If you send the same
message_ref_idagain after the first send finished, you get the original result back with"duplicate": true, and nothing is sent twice. - If the first send is still in progress, you get
409 SEND_IN_PROGRESS. Wait a moment and retry. - If the first attempt ended in
502 SEND_UNCONFIRMED, the id is released so a retry can go through.
This makes it safe to retry after a timeout or a network error.
Check a message's status
Look up any message by the message_id from the send response.
curl https://chatmaxima.com/api/v2/messages/123456/ \
-H "Authorization: YOUR_API_KEY"
{
"status": "success",
"code": 200,
"data": {
"message_id": "123456",
"provider_message_id": "wamid.XXXX",
"account_id": "a1b2c3d4e5f6",
"channel": "fb_whatsapp",
"contact_id": "ct_abc123",
"conversation_id": "conv_abc123",
"direction": "outbound",
"status": "delivered",
"error": null,
"sent_at": "2026-11-03T10:15:00Z"
}
}
status moves through sent, delivered and read as the channel reports back, or becomes failed, with the channel's reason in error. For WhatsApp you can receive these updates as they happen with WhatsApp Webhooks instead of polling.
Errors
| HTTP | Code | When it happens |
|---|---|---|
| 400 | INVALID_JSON | The body is not a JSON object. |
| 400 | MISSING_REQUIRED_FIELDS | account_id is missing, or none of contact_id, contact_ref_id and to was sent. |
| 403 | PLAN_EXPIRED | Your plan has ended. |
| 403 | MESSAGE_LIMIT_REACHED | Your monthly message allowance is used up. |
| 404 | ACCOUNT_NOT_FOUND | account_id is not a channel account of this workspace. |
| 404 | CONTACT_NOT_ON_CHANNEL | The contact does not exist on this channel account. |
| 404 | UNKNOWN_CONVERSATION | Web chat conversation_id does not belong to this contact. |
| 404 | MESSAGE_NOT_FOUND | Status lookup: no message with that id in this workspace. |
| 409 | SEND_IN_PROGRESS | A send with the same message_ref_id is still running. |
| 422 | CHANNEL_NOT_SUPPORTED | The account's channel cannot send through this API. |
| 422 | ACCOUNT_INACTIVE | The channel account is not active, for example pending verification. |
| 422 | VALIDATION_ERROR | A field is invalid, for example text over 4,096 characters or a bad phone number. |
| 422 | MEDIA_URL_NOT_ALLOWED | The media URL was not uploaded with the Media Upload API, or could not be fetched. |
| 422 | OUTSIDE_MESSAGING_WINDOW | The 24-hour window is closed on WhatsApp, Instagram or Messenger. |
| 422 | SEND_REJECTED | The channel refused the message. details carries its reason. |
| 502 | SEND_UNCONFIRMED | The channel did not confirm the send. Retry with the same message_ref_id. |
Authentication, plan and rate-limit errors are listed on the API overview.
Best Practices
- Always set
message_ref_idso a retry after a timeout never sends the message twice. - Check the window before you build a campaign on free-form messages. Outside 24 hours, WhatsApp needs a template.
- Store
message_idwith your own record so you can match delivery updates later. - Keep sends under 300 per minute per API key, the rate limit for send endpoints.