Send customer messages
Queue customer notifications, confirmations, reminders, and support messages.
What the endpoint does
The send endpoint validates the API key, verifies sender ownership, checks plan and sender limits, stores a message record, and queues delivery.
A successful response means the message was accepted into the queue. It does not mean the recipient has already received it. Use the message status endpoint or dashboard history for delivery state.
Endpoint reference
The request must include `receiver_number` and either `message` or `attachment`. Omit `sender_id` for managed Free Sandbox or automatic platform routing. Include it when a paid integration selects an assigned connected number.
Text messages can be up to 4096 characters. Attachments are base64-encoded, limited by the request schema and the decoded storage limit.
/api/messages/sendQueue an outbound WhatsApp customer message.
| Parameter | Type | Description |
|---|---|---|
x-api-keyrequired | header | API key with the `send_message` role. |
sender_id | uuid | Paid mode only: an assigned connected sender. Omit for managed routing. |
receiver_numberrequired | string | Destination phone number. |
message | string | Message body, max 4096 characters. Required unless attachment is present. |
attachment.file_name | string | Attachment name, max 180 characters. |
attachment.mime_type | string | Attachment content type, max 120 characters. |
attachment.content_base64 | string | Base64 content, max 10,000,000 characters. |
priority | integer | Queue priority from 1 to 10. |
source_system | string | Integration name, max 80 characters. |
source_reference | string | Your record ID, max 120 characters. |
idempotency_key | string | Duplicate protection key, max 160 characters. |
{
"receiver_number": "212612345678",
"message": "Your appointment is confirmed for tomorrow at 10:00.",
"source_system": "appointments",
"source_reference": "booking_1042",
"idempotency_key": "booking_1042_confirmation"
}{
"message_id": "2b7a0bd5-1a0d-4e6c-85b1-a7f44f92dfb0",
"workspace_id": "f2aa1d49-9f4b-4f37-9918-d8d8f13fb530",
"status": "queued",
"duplicate": false
}Attachment messages
Use `attachment` when sending an image or document. The backend accepts either raw base64 or a `data:*;base64,` URL and stores the decoded file before delivery.
Attachment access, monthly count, total media storage, maximum decoded file size, and retention period are controlled by the workspace plan. Expired files are removed automatically. Dashboard media links require an authorized customer session and are not public object-storage URLs. Use the Usage & Limits page to see the limits that apply to your workspace.
- Supported images: image/jpeg, image/png, image/webp, image/gif.
- Supported documents: application/pdf, text/plain, text/csv, application/msword, application/vnd.ms-excel.
- Supported Office documents: application/vnd.openxmlformats-officedocument.wordprocessingml.document and application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.
- Video and audio attachments are not supported. Send a secure link instead.
- Too large fix: compress the file, upload a smaller file, or send a link instead of an attachment.
- Invalid base64 fix: send only valid base64 or a valid data URL with no corrupted characters.
{
"receiver_number": "212612345678",
"message": "Invoice attached.",
"attachment": {
"file_name": "invoice-1042.pdf",
"mime_type": "application/pdf",
"content_base64": "JVBERi0xLjQK..."
},
"source_system": "billing",
"source_reference": "invoice_1042",
"idempotency_key": "invoice:1042:sent"
}Laravel example
Wrap HookMessage behind a service class so controllers do not know about headers, retry logic, or provider details.
use Illuminate\Support\Facades\Http;
$response = Http::baseUrl(config('services.hookmessage.url'))
->withHeaders([
'x-api-key' => config('services.hookmessage.key'),
])
->post('/api/messages/send', array_filter([
'sender_id' => config('services.hookmessage.sender_id'),
'receiver_number' => $order->customer_phone,
'message' => "Your order {$order->number} is confirmed.",
'source_system' => 'checkout',
'source_reference' => (string) $order->id,
'idempotency_key' => "order:{$order->id}:confirmed",
], fn ($value) => $value !== null));
$response->throw();
$message = $response->json();Need help?
Use Book Integration Help if you want setup help for OTP or automated customer messaging.
Continue building
Next steps
Message status and history
Use message IDs to trace status, delivery attempts, timestamps, and failure reasons.
Complete integration examples
Use these end-to-end examples after cURL succeeds.
Webhooks and automation
Register webhook endpoints per API client and subscribe to the events your system needs.
Troubleshooting
Use this page when a new integration does not send, status stays queued, or webhook verification fails.