Send WhatsApp messages
Send any WhatsApp Business message — text, rich media, locations, contacts, reactions and interactive buttons or lists — through the same Sozuri endpoint you use for SMS. One API, every conversation.
snake_case field names instead of the camelCase used elsewhere in the API.
- Request body shape
- Response shape
- Text messages
- Reactions
- Media (image, video, audio, document, sticker)
- Location
- Contact cards
- Interactive (buttons & lists)
- Replies (quoting another message)
- Error responses
Request body shape
Every WhatsApp request shares the same wrapper. Set type to the content type you want to send and include the matching object (text, image, location, etc.):
Required headers
POST /api/v1/messaging
Content-Type: application/json
Authorization: Bearer Your_Project_API_KEY
Common request envelope
{
"project": "YOUR_PROJECT_NAME",
"from": "WHATSAPP_BUSINESS_NUMBER",
"to": "TO_PHONE_NUMBER",
"campaign": "YOUR_CAMPAIGN_NAME",
"channel": "whatsapp",
"recipient_type": "individual",
"type": "TYPE",
// include exactly ONE of these, depending on type
"text": { },
"reaction": { },
"image": { },
"location": { },
"contacts": [ ],
"interactive": { }
}
| Field | Required | Type | Description |
|---|---|---|---|
| project | Yes | String | The Sozuri project that owns the API key making this request. |
| from | Yes | String | Your WhatsApp Business number, in E.164 format. |
| to | Yes | String | A comma-separated list of recipient numbers in E.164. Sozuri will normalise 0722-503-129 to 254722503129. |
| channel | Yes | String | Always whatsapp on this endpoint. |
| recipient_type | Yes | String | Always individual. |
| type | Yes | String | One of text, reaction, image, video, audio, document, sticker, location, contacts, interactive. |
| campaign | No | String | Optional label for reporting and analytics. |
| text / reaction / image / video / audio / document / sticker / location / contacts / interactive | One of these | Object | The content payload — one matches the type you chose. See each sample below. |
| apiKey | — | String | Your project API key. Recommended: send as a Bearer token in the Authorization header instead. |
Response shape
Every WhatsApp request returns the same synchronous JSON envelope — one entry per recipient:
{
"messageData": {
"messages": 1
},
"recipients": [
{
"message_id": "f659b9ba4d9551c39ca61498126da29f68a1c1e9",
"to": "+254725164293",
"status": "queued"
}
]
}
| Field | Type | Description |
|---|---|---|
| messageData.messages | Number | Total messages accepted for delivery. |
| recipients[].message_id | String | WhatsApp’s unique ID for the message. Use this to correlate delivery and read callbacks. |
| recipients[].to | String | The recipient’s phone number. |
| recipients[].status | String | Initial acceptance status (queued, accepted, unknown_number…). Final delivery status arrives via webhook. |
Text message
The most common message type — up to 4,096 characters of plain (or richly formatted) text. Optionally enable URL previews.
curl -X POST 'https://sozuri.net/api/v1/messaging' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"project": "YOUR_PROJECT_NAME",
"from": "BUSINESS_PHONE_NUMBER",
"channel": "whatsapp",
"recipient_type": "individual",
"campaign": "YOUR_CAMPAIGN_NAME",
"to": "PHONE_NUMBER",
"type": "text",
"text": {
"preview_url": false,
"body": "MESSAGE_CONTENT"
}
}'
Reaction
React to a customer’s previous message with an emoji — great for lightweight acknowledgements (“got it”, “thanks”, “loved that photo”).
curl -X POST 'https://sozuri.net/api/v1/messaging' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"project": "YOUR_PROJECT_NAME",
"from": "BUSINESS_PHONE_NUMBER",
"channel": "whatsapp",
"recipient_type": "individual",
"campaign": "YOUR_CAMPAIGN_NAME",
"to": "PHONE_NUMBER",
"type": "reaction",
"reaction": {
"message_id": "MESSAGE_ID",
"emoji": "๐"
}
}'
Media messages
Send an image, video, audio, document or sticker by setting type to the media kind and providing a link to a publicly accessible file.
curl -X POST 'https://sozuri.net/api/v1/messaging' \
-H 'Authorization: Bearer apiKey' \
-H 'Content-Type: application/json' \
-d '{
"project": "YOUR_PROJECT_NAME",
"from": "BUSINESS_PHONE_NUMBER",
"channel": "whatsapp",
"recipient_type": "individual",
"campaign": "YOUR_CAMPAIGN_NAME",
"to": "PHONE_NUMBER",
"type": "image",
"image": {
"link": "https://IMAGE_URL"
}
}'
Location
Send a pinned location with optional name and address — perfect for “here’s our shop”, “your delivery driver is here”, or sharing a meeting point.
curl -X POST 'https://sozuri.net/api/v1/messaging' \
-H 'Authorization: Bearer apiKey' \
-H 'Content-Type: application/json' \
-d '{
"project": "YOUR_PROJECT_NAME",
"from": "BUSINESS_PHONE_NUMBER",
"channel": "whatsapp",
"recipient_type": "individual",
"campaign": "YOUR_CAMPAIGN_NAME",
"to": "PHONE_NUMBER",
"type": "location",
"location": {
"longitude": LONG_NUMBER,
"latitude": LAT_NUMBER,
"name": "LOCATION_NAME",
"address": "LOCATION_ADDRESS"
}
}'
Contact card
Share one or more contact cards — the recipient can save the contact straight to their phone with a tap. Useful for support escalations, referrals, or sharing a sales rep’s details.
curl -X POST 'https://sozuri.net/api/v1/messaging' \
-H 'Authorization: Bearer apiKey' \
-H 'Content-Type: application/json' \
-d '{
"project": "YOUR_PROJECT_NAME",
"from": "BUSINESS_PHONE_NUMBER",
"channel": "whatsapp",
"recipient_type": "individual",
"campaign": "YOUR_CAMPAIGN_NAME",
"to": "PHONE_NUMBER",
"type": "contacts",
"contacts": [{
"addresses": [
{ "street": "STREET", "city": "CITY", "state": "STATE", "zip": "ZIP", "country": "COUNTRY", "country_code": "COUNTRY_CODE", "type": "HOME" },
{ "street": "STREET", "city": "CITY", "state": "STATE", "zip": "ZIP", "country": "COUNTRY", "country_code": "COUNTRY_CODE", "type": "WORK" }
],
"birthday": "YEAR_MONTH_DAY",
"emails": [
{ "email": "EMAIL", "type": "WORK" },
{ "email": "EMAIL", "type": "HOME" }
],
"name": {
"formatted_name": "NAME",
"first_name": "FIRST_NAME",
"last_name": "LAST_NAME",
"middle_name": "MIDDLE_NAME",
"suffix": "SUFFIX",
"prefix": "PREFIX"
},
"org": {
"company": "COMPANY",
"department": "DEPARTMENT",
"title": "TITLE"
},
"phones": [
{ "phone": "PHONE_NUMBER", "type": "HOME" },
{ "phone": "PHONE_NUMBER", "type": "WORK", "wa_id": "PHONE_OR_WA_ID" }
],
"urls": [
{ "url": "URL", "type": "WORK" },
{ "url": "URL", "type": "HOME" }
]
}]
}'
Interactive: lists & buttons
Interactive messages let customers tap their reply instead of typing it. There are two flavours:
- List messages — up to 10 selectable rows grouped into sections. Best for menus, catalogues and structured choices.
- Reply buttons — up to 3 quick-reply buttons. Best for yes/no, confirm/cancel and binary choices.
List message
curl -X POST 'https://sozuri.net/api/v1/messaging' \
-H 'Authorization: Bearer apiKey' \
-H 'Content-Type: application/json' \
-d '{
"project": "YOUR_PROJECT_NAME",
"from": "BUSINESS_PHONE_NUMBER",
"channel": "whatsapp",
"recipient_type": "individual",
"campaign": "YOUR_CAMPAIGN_NAME",
"to": "PHONE_NUMBER",
"type": "interactive",
"interactive": {
"type": "list",
"header": { "type": "text", "text": "HEADER_TEXT" },
"body": { "text": "BODY_TEXT" },
"footer": { "text": "FOOTER_TEXT" },
"action": {
"button": "BUTTON_TEXT",
"sections": [
{
"title": "SECTION_1_TITLE",
"rows": [
{ "id": "SECTION_1_ROW_1_ID", "title": "SECTION_1_ROW_1_TITLE", "description": "SECTION_1_ROW_1_DESCRIPTION" },
{ "id": "SECTION_1_ROW_2_ID", "title": "SECTION_1_ROW_2_TITLE", "description": "SECTION_1_ROW_2_DESCRIPTION" }
]
},
{
"title": "SECTION_2_TITLE",
"rows": [
{ "id": "SECTION_2_ROW_1_ID", "title": "SECTION_2_ROW_1_TITLE", "description": "SECTION_2_ROW_1_DESCRIPTION" },
{ "id": "SECTION_2_ROW_2_ID", "title": "SECTION_2_ROW_2_TITLE", "description": "SECTION_2_ROW_2_DESCRIPTION" }
]
}
]
}
}
}'
Reply buttons
curl -X POST 'https://sozuri.net/api/v1/messaging' \
-H 'Authorization: Bearer apiKey' \
-H 'Content-Type: application/json' \
-d '{
"project": "YOUR_PROJECT_NAME",
"from": "BUSINESS_PHONE_NUMBER",
"channel": "whatsapp",
"recipient_type": "individual",
"campaign": "YOUR_CAMPAIGN_NAME",
"to": "PHONE_NUMBER",
"type": "interactive",
"interactive": {
"type": "button",
"body": { "text": "BUTTON_TEXT" },
"action": {
"buttons": [
{ "type": "reply", "reply": { "id": "UNIQUE_BUTTON_ID_1", "title": "BUTTON_TITLE_1" } },
{ "type": "reply", "reply": { "id": "UNIQUE_BUTTON_ID_2", "title": "BUTTON_TITLE_2" } }
]
}
}
}'
Reply to a previous message
To quote an earlier message in the conversation, add a context object with the original message_id. WhatsApp will render the original above your new message as a contextual bubble.
curl -X POST 'https://sozuri.net/api/v1/messaging' \
-H 'Authorization: Bearer apiKey' \
-H 'Content-Type: application/json' \
-d '{
"project": "YOUR_PROJECT_NAME",
"from": "BUSINESS_PHONE_NUMBER",
"channel": "whatsapp",
"campaign": "YOUR_CAMPAIGN_NAME",
"context": {
"message_id": "MESSAGE_ID"
},
"to": "PHONE_NUMBER",
"type": "text",
"text": {
"preview_url": false,
"body": "your-text-message-content"
}
}'
Error responses
The WhatsApp endpoint uses several error envelope shapes depending on where the failure occurs. Auth errors (Unknown project.) follow the SMS envelope and are documented on the Authentication page.
| Condition | HTTP | Response body |
|---|---|---|
| Project credit balance is too low to send the message. | 400 | |
The sending business from number isn’t configured on any WhatsApp account in this project. |
400 | |
| The project has no resolvable WhatsApp Business Account (WABA) link. | 400 | |
The context.message_id on a reply (or reaction.message_id on a reaction) doesn’t match a message in this project. |
422 | |
| Sandbox sender: recipient hasn’t been registered as a sandbox test number. | 422 | |
| Database persistence failure after acceptance. | 500 | |
Re-engagement message, template rejected, etc.) arrive on the delivery webhook, not the send response. See WhatsApp delivery webhook for the failure payload shape.
Use cases
The conversations WhatsApp powers for businesses across Kenya.
Conversational commerce
Send catalogues as interactive lists, accept orders via buttons, and confirm payment in a single thread.
Order tracking with a live map
Push a location pin when the rider sets off and update with a media message on delivery.
Support & ticketing
Reply to a customer’s photo of a damaged item using the reply API, then escalate with a contact card for their support agent.
Surveys with interactive buttons
Replace plain-text Y/N replies with tappable buttons — lifts response rates and removes typos.
Bring your business onto WhatsApp.
Get your business profile verified, connect your number, and start sending in days — we handle the Meta paperwork.