Send a Message
Send a WhatsApp or SMS message via the Messaging API.
Send a Message
A single endpoint sends every message type — text, media, template, or interactive. The message_type field determines which fields are required.
Endpoint
POST https://api.tryvox.io/v1/messaging/messagesRequest body
| Field | Type | Required | Description |
|---|---|---|---|
channel_id | uuid | yes | The channel to send from. See Channels. |
to | string | yes | Recipient phone number, E.164. |
message_type | string | yes | text, image, audio, video, document, template, interactive. |
content | string | conditional | Message text. Required for text. Optional caption for image / video / document. |
media_url | string | conditional | Public URL of the media file. Required for image, audio, video, document. |
media_mime_type | string | conditional | MIME type of the media (e.g. image/jpeg, application/pdf). Required when media_url is set. |
template_name | string | conditional | Name of the registered template. Required when message_type is template. |
template_params | object | conditional | Template language + components (Meta's shape). Required for template. |
First contact with a recipient on WhatsApp must use a
template. The 24-hour customer-service window opens after the recipient replies, after whichtextand media types are allowed.
Examples
Plain text (in 24-hour window)
curl -X POST https://api.tryvox.io/v1/messaging/messages \
-u $TRYVOX_AUTH_ID:$TRYVOX_AUTH_TOKEN \
-H "Content-Type: application/json" \
-d '{
"channel_id": "8f7a4b2e-91c2-4f3d-9a1e-2c4b6d8f0a1c",
"to": "+14155551234",
"message_type": "text",
"content": "Thanks for getting back to us. How can we help today?"
}'Image with caption
curl -X POST https://api.tryvox.io/v1/messaging/messages \
-u $TRYVOX_AUTH_ID:$TRYVOX_AUTH_TOKEN \
-H "Content-Type: application/json" \
-d '{
"channel_id": "8f7a4b2e-91c2-4f3d-9a1e-2c4b6d8f0a1c",
"to": "+14155551234",
"message_type": "image",
"media_url": "https://your-cdn.example/invoice-april.png",
"media_mime_type": "image/png",
"content": "Your April invoice"
}'Template (first contact, OTP)
curl -X POST https://api.tryvox.io/v1/messaging/messages \
-u $TRYVOX_AUTH_ID:$TRYVOX_AUTH_TOKEN \
-H "Content-Type: application/json" \
-d '{
"channel_id": "8f7a4b2e-91c2-4f3d-9a1e-2c4b6d8f0a1c",
"to": "+14155551234",
"message_type": "template",
"template_name": "login_otp",
"template_params": {
"language": "en_US",
"components": [
{
"type": "body",
"parameters": [{"type": "text", "text": "284731"}]
}
]
}
}'The template_params object is passed through to the Meta WhatsApp Cloud API verbatim — see Meta's template reference for component structure.
Response
201 Created:
{
"id": "1b2c3d4e-5f60-7180-9a2b-3c4d5e6f7a8b",
"channel_id": "8f7a4b2e-91c2-4f3d-9a1e-2c4b6d8f0a1c",
"conversation_id": "3c4d5e6f-7a8b-9c0d-1e2f-3a4b5c6d7e8f",
"direction": "outbound",
"to": "+14155551234",
"message_type": "text",
"content": "Thanks for getting back to us. How can we help today?",
"status": "queued",
"created_at": "2026-04-09T10:30:00Z"
}The send is queued — the response returns before delivery confirmation. Track lifecycle via webhook events on the channel:
message.sent— accepted by Metamessage.delivered— handset receivedmessage.read— recipient opened (only if read receipts are enabled)message.failed— delivery failed
Subscribe in Notifications → Webhooks.
Errors
| Status | Code | Reason |
|---|---|---|
| 400 | VALIDATION_FAILED | Missing required field for the chosen message_type, or to not E.164 |
| 401 | INVALID_CREDENTIALS | Auth ID / Auth Token bad |
| 403 | CHANNEL_INACTIVE | The channel is disabled or its Meta access token is rejected |
| 403 | OUTSIDE_CS_WINDOW | Tried text/media to a recipient who hasn't replied within 24h. Use template. |
| 403 | DLT_NOT_REGISTERED | SMS to +91 and no matching DLT template (see DLT) |
| 404 | CHANNEL_NOT_FOUND | channel_id doesn't belong to this tenant |
| 404 | TEMPLATE_NOT_FOUND | template_name not registered on this channel — sync via Templates |
| 429 | RATE_LIMITED | Per-channel send rate exceeded — retry with the Retry-After header |
Notes
- One conversation per (channel, recipient) pair. The first message creates the conversation; subsequent sends reuse
conversation_id. - Idempotency is not exposed yet. Retrying the same payload duplicates the message. Coalesce on your side or wait for the lifecycle webhook before retrying.
- Media URLs must be reachable from Meta's infrastructure, not just from TryVox. Public S3 buckets / CDNs are fine; localhost tunnels often aren't.
- Outbound SMS uses the same endpoint with a
textmessage type and a channel whose underlying carrier is SMS-capable. The DLT validator runs for+91destinations only.