Skip to Content

Send WhatsApp Messages

Send an approved WhatsApp template message to any phone number.

POST /v1/whatsapp/send

Request body

FieldTypeRequiredDescription
tostringRecipient phone number in E.164 format (e.g. +254700000000)
phone_number_idstringYour sending phone number id from /v1/whatsapp/phone_numbers
template_namestringThe template name from the templates endpoint
language_codestringThe template language from the templates endpoint (e.g. en_US)
variablesobjectTemplate placeholder values. See below.
media_urlstringPublic URL of media to use as template header. Alternative to media_id. See Handling Media.
media_idstringmedia_id from the Upload Media endpoint. Alternative to media_url. See Handling Media.

Use the exact template_name and language_code as returned by the templates endpoint. Phone numbers are identified by their phone number ID (id field), not the phone number itself.

Response

{ "success": true, "message_id": "wamid.HBgNMjU0NzAwMDAwMDAwFQIAERgSNDQ1...", "to": "+254700000000", "template": "order_confirmation" }

A successful response means the request was accepted — it does not guarantee delivery. Use webhooks to track delivery status (sent, delivered, read, failed).


Passing template variables

Fetch the template list first, then read expected_variables to know what to pass in variables. Use each param_name as the key.

No variables

If the template has no placeholders, omit the variables field entirely.

curl -X POST https://api.bahasha.app/v1/whatsapp/send \ -H "Authorization: Bearer bh_live_xxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "to": "+254700000000", "phone_number_id": "123456789", "template_name": "hello_world", "language_code": "en_US" }'

Named variables

Placeholders use string names like {{customer_name}}. The param_name is a string — use it as the key.

// expected_variables.body: // [ { "param_name": "customer_name", "example": "John Doe" }, // { "param_name": "order_id", "example": "ORD-12345" } ]
curl -X POST https://api.bahasha.app/v1/whatsapp/send \ -H "Authorization: Bearer bh_live_xxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "to": "+254700000000", "phone_number_id": "123456789", "template_name": "order_confirmation", "language_code": "en_US", "variables": { "body": { "customer_name": "Jane Smith", "order_id": "ORD-78901" } } }'

Positional variables

Placeholders use numbers like {{1}}, {{2}}. The param_name is a number — use the number as a string key.

// expected_variables: // header: [ { "param_name": 1, "example": "Acme Corp" } ] // body: [ { "param_name": 1, "example": "John" }, // { "param_name": 2, "example": "2026-03-01" } ]
curl -X POST https://api.bahasha.app/v1/whatsapp/send \ -H "Authorization: Bearer bh_live_xxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "to": "+254700000000", "phone_number_id": "123456789", "template_name": "appointment_reminder", "language_code": "en_US", "variables": { "header": { "1": "Acme Corp" }, "body": { "1": "John", "2": "2026-03-01" } } }'

Authentication (OTP) templates

Authentication templates always have a single body variable with param_name: 1. Pass the OTP code as variables.body["1"].

// expected_variables.body: // [ { "param_name": 1, "example": "123456" } ]
curl -X POST https://api.bahasha.app/v1/whatsapp/send \ -H "Authorization: Bearer bh_live_xxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "to": "+254700000000", "phone_number_id": "123456789", "template_name": "your_otp_template", "language_code": "en_US", "variables": { "body": { "1": "847291" } } }'

Sending with media

For templates with an image, video, or document header, you can override the default media per send request. See Handling Images, Videos, and Documents for full details and upload instructions.

# Pass any publicly accessible URL — no upload step needed curl -X POST https://api.bahasha.app/v1/whatsapp/send \ -H "Authorization: Bearer bh_live_xxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "to": "+254700000000", "phone_number_id": "123456789", "template_name": "promo_banner", "language_code": "en_US", "media_url": "https://cdn.example.com/images/summer-sale.jpg" }'

If neither media_url nor media_id is provided, the template’s default media is used automatically. Providing both will return an error.