Skip to main content
POST /messages sends all four message types. The API responds right away with 202 and a jobId, and sends the message in the background. You then check the jobId to find out whether the message went out.

The flow

1

Find the outbound channel

Every send needs a channelId. List the company’s connected channels:
Keep the id in your integration’s configuration. It almost never changes, and fetching the list on every send uses up your per-minute limit for no reason.
2

Send

The response comes back right away:
Always send Idempotency-Key on this endpoint. If the call times out and you try again without the key, the customer may get the message twice. See Idempotency.
3

Confirm the result

Keep the ticketId. With it you follow the conversation from then on, using GET /tickets/{ticketId}/messages.

Job states

status
Accepted and waiting to be processed.
status
Being sent to the provider.
status
Delivered to the provider. The providerMessageId field carries the identifier the provider gave the message.
status
The send failed. failedReason explains why and attempts shows how many times the send was tried.
Monthly active contacts. The plan has a limit of active contacts per month: a contact counts once, at the first message exchanged with the company in the month (sent or received), and the count resets on the 1st. Once the limit is reached, contacts that already counted keep receiving normally; a message to a new contact is refused on the call with 402 ERR_API_ACTIVE_CONTACTS_LIMIT_REACHED. If the limit runs out between the call and the send, the job ends as failed with failedReason: ERR_MAX_ACTIVE_CONTACTS_BY_MONTH_REACHED. web_whatsapp channels are not limited.
sent means the provider accepted the message. It does not yet mean the recipient received or read it. The delivery and read confirmation comes later, in the message’s status inside the ticket.
When checking the job, start with a 1 second interval between checks and keep doubling it. Checking over and over with no pause uses up the whole per-minute limit in a few seconds.

The four types

This is the default type. Besides the recipient, only body is required.
The body field accepts system variables, such as {{name}}.

Who to send to

There are three ways to specify the recipient. If more than one is sent, this order applies: ticketId, contactId, number.

ticketId

Sends within a ticket that already exists, and the message stays in the same conversation history. Use it to reply to a conversation in progress.

contactId

Sends to a contact that already exists, on any channel. Required on channels without a phone number (Instagram, Facebook/Messenger, Webchat, Telegram): on them you can only message someone who has already talked to the channel. On Webchat, the visitor must have opened this channel’s webchat.

number

The recipient’s number, on phone channels. If the contact does not exist yet, it is created, and you can use contactName to give it a name. If there is no open ticket with that contact, a new one is opened.

Controlling the ticket in the same send

Four optional fields change the ticket in the same call as the send, with no need for a second request:
integer
Moves the ticket to this queue after sending.
boolean
Reopens the ticket if it is closed.
boolean
Closes the ticket after sending. Useful for notices that expect no reply.
integer
Wrap-up type recorded when the ticket is closed. Use it together with closeTicket.

High volume

Do not call POST /messages over and over to send in bulk. Each call consumes the whole company’s per-minute limit, and pacing, retries and reporting are left up to you.That is what the campaigns module is for. It controls the outbound pace, lets you pause, resume and cancel the send, and shows a delivery report at the end.

Idempotency

How to keep a retry from duplicating the message.

Messages reference

Every accepted field, with a playground to test.