Messaging API
Send a message
Send a free-form WhatsApp message inside an open 24h window.
POST /api/v1/messages/sendSends a free-form WhatsApp message. Only works inside an open 24-hour window — the contact must have messaged you in the last 24h. For outbound-first messaging use Send a template.
Body
The format is Meta-like: a type plus an object named after that type.
| Field | Type | Required | Notes |
|---|---|---|---|
to | string | required | Recipient phone, country code included. Non-digits are stripped; validated as a possible number. |
type | string | required | One of text, image, video, audio, document. |
text.body | string | required if type=text | Message text, non-empty. |
<type>.link | string (URL) | required if type ≠ text | Media URL for image/video/audio/document. |
<type>.caption | string | optional | Used only for image and video; ignored for audio/document. |
Optional header
| Header | Notes |
|---|---|
Idempotency-Key | If a previous request used the same value, the message is not re-sent — returns 201 with a note. |
Request
curl -X POST https://api.getkanal.com/api/v1/messages/send \
-H "Authorization: Bearer 8fK2pX9mWq4Ld7Vb3Nc6Ts1Z" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7f9c2b1e-2b8a-4e5f-9c1d-aa0011223344" \
-d '{
"to": "33612345678",
"type": "text",
"text": { "body": "Hello from the API!" }
}'Media example:
curl -X POST https://api.getkanal.com/api/v1/messages/send \
-H "Authorization: Bearer 8fK2pX9mWq4Ld7Vb3Nc6Ts1Z" \
-H "Content-Type: application/json" \
-d '{
"to": "33612345678",
"type": "image",
"image": {
"link": "https://example.com/photo.jpg",
"caption": "Optional caption"
}
}'Response 200 OK
{
"success": true,
"message": "Message sent successfully",
"message_id": 123456,
"contact_id": 7890,
"phone_number": "33612345678"
}When an Idempotency-Key matches a prior request, you get 201:
{ "message": "Message already sent with this idempotency key: <key>" }Errors
All errors use the flat { "error": "..." } shape (this endpoint has no
422 validation envelope).
| Status | Body | Cause |
|---|---|---|
401 | { "error": "API key is missing" } / { "error": "Invalid API key" } | Auth. |
403 | { "error": "Team has no active subscription" } | No active subscription. |
400 | { "error": "type is required and must be one of: text, image, video, audio, document" } | Bad/missing type. |
400 | { "error": "text.body is required for type text" } | Missing text body. |
400 | { "error": "<type>.link is required for type <type>" } | Missing media link. |
400 | { "error": "to is required" } / { "error": "Phone number is not valid" } | Bad recipient. |
400 | { "error": "Contact has opted out from receiving messages", "contact_id": …, "phone_number": "…" } | Recipient opted out. |
400 | { "error": "Cannot send free-form message: no active conversation window…", "contact_id": …, "phone_number": "…" } | No open 24h window — use a template. |
400 | { "error": "Error sending message", "details": "…" } | WhatsApp send failed. |