POST /v1/messages/send
Send a text or media message through a connected channel.
ℹ️ Sending is asynchronous: messages go into a queue and are sent within a few seconds. A successful response (202) means the message has been accepted for processing, not that it has already been delivered.
URL
POST https://api.wazzabee.com/api-gateway/v1/messages/send
Headers
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Request body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
channelId | string (UUID) | ✅ Yes | Channel ID. Get it through GET /v1/channels |
chatId | string | ✅ Yes | Recipient identifier: a phone number (for WhatsApp) or a chat_id (for Telegram) |
text | string | ✅* | Message text. Required if mediaUrl is not set |
mediaUrl | string (URL) | ✅* | Direct link to a media file (image, video, document). Required if text is not set |
mediaType | string | Conditional | Media type: photo, video, document, audio. Required whenever mediaUrl is set |
caption | string | No | Caption for the media file. Works only together with mediaUrl |
senderName | string | No | Author name: who sent the message. Your agents see it in the WazzaBee chat and it comes back in the webhook. It is not passed on to the customer's messenger |
userId | string | No | The employee's identifier in your CRM. We return it as is: there is no need to create that employee in WazzaBee |
automated | boolean | No | true: the message was sent by a robot, an auto-reply or a reminder. Such a message is marked as automated in the chat |
campaignId | string | No | Your campaign label, up to 100 characters. It is returned in the response and in the outgoing, status and button click webhooks. You can filter GET /v1/messages by it and export sends in the dashboard: «Connectors» → «API» → «Export of API sends» |
* You need to set either text or mediaUrl, at least one of the two.
Who sent the message
By default a message sent through the API is signed in the chat simply as "via API", with no name. If your CRM knows the author, pass it along: the message will show "via API · Nazerke A." underneath, and the name will come back to you in the messages_outgoing webhook. That way the chat feed makes it clear which agent replied to the customer.
curl -X POST "https://api.wazzabee.com/api-gateway/v1/messages/send" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channelId": "550e8400-e29b-41d4-a716-446655440000",
"chatId": "77001234567",
"text": "Hello! Your documents are ready.",
"senderName": "Nazerke Akhmetova",
"userId": "manager-42"
}'
Instead of senderName and userId you can pass an author object, the result is the same:
{
"channelId": "550e8400-e29b-41d4-a716-446655440000",
"chatId": "77001234567",
"text": "Hello!",
"author": { "id": "manager-42", "name": "Nazerke Akhmetova" }
}
Mark messages that are not sent by a person (robots, auto-replies, reminders) with "automated": true. In the chat they will be shown separately from agent replies.
ℹ️ The signature is visible only to you. The author's name is shown to your agents in the WazzaBee chat and comes through in webhooks. It is not passed on to the customer in WhatsApp or Telegram: they only see the message text.
chatId format
| Channel type | chatId format | Example |
|---|---|---|
| WhatsApp (QR) | Phone number with country code, no + | 77001234567 |
| WhatsApp (WABA) | Phone number with country code, no + | 77001234567 |
| Telegram Bot | The user's numeric chat_id | 123456789 |
| Telegram Personal | Numeric chat_id or username | 123456789 |
Request examples
Text message
curl -X POST "https://api.wazzabee.com/api-gateway/v1/messages/send" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channelId": "550e8400-e29b-41d4-a716-446655440000",
"chatId": "77001234567",
"text": "Hello! Your order #1234 is ready for pickup."
}'
Image with a caption
curl -X POST "https://api.wazzabee.com/api-gateway/v1/messages/send" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channelId": "550e8400-e29b-41d4-a716-446655440000",
"chatId": "77001234567",
"mediaUrl": "https://example.com/photo.jpg",
"mediaType": "photo",
"caption": "A photo of your order"
}'
Document
curl -X POST "https://api.wazzabee.com/api-gateway/v1/messages/send" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channelId": "550e8400-e29b-41d4-a716-446655440000",
"chatId": "77001234567",
"mediaUrl": "https://example.com/invoice.pdf",
"mediaType": "document"
}'
Successful response (202 Accepted)
{
"success": true,
"queued": true,
"queueId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"message": "The message has been queued for sending"
}
| Field | Type | Description |
|---|---|---|
success | boolean | Always true on success |
queued | boolean | The message has been accepted into the queue |
queueId | string (UUID) | Unique ID in the sending queue. The same queueId comes in the delivery status webhook (messages_status) and in GET /v1/messages: use it to find out whether the message was delivered |
campaignId | string | null | Campaign label, if you passed one |
message | string | A text description of the result |
Possible errors
| Code | Error | Reason |
|---|---|---|
400 | Invalid JSON in the request body | The request body is not valid JSON |
400 | The channelId field is required | The channelId parameter was not passed |
400 | The chatId field is required | The chatId parameter was not passed |
400 | You must set text or mediaUrl | Neither a text nor a media file was passed |
400 | The channel is inactive | The channel is disabled or not set up |
400 | Unsupported channel type | This channel type is not supported by the API |
403 | The channel is not linked to this integration | The channel is not among those allowed for this API key |
404 | Channel not found or no access | The channel does not exist or belongs to another user |
How it works under the hood
- The API checks authorization and access to the channel
- It looks for the contact by
chatId(phone or external_id). If there is none, it creates a new one - It looks for an existing conversation. If there is none, it creates a new one
- It picks the delivery method based on the channel type
- It puts the message into the
message_queue - A background process takes the message out of the queue and sends it through the channel
