Sending Messages
Send WhatsApp template messages using approved templates. This endpoint delivers messages to recipients using your WhatsApp Business number.
Send Template Message
Send a template-based WhatsApp message to one or more recipients.
Endpoint
POST /v1/whatsapp/sendRequest Body
{
"destination": "0720000000",
"template_id": "115XXXXXXXXXX295",
"phone_number_id": "46646XXXXXXX268",
"component_list": {
"components": [
{
"component_type": "BODY",
"fields": [
{ "name": "1", "value": "Peter Pan" },
{ "name": "2", "value": "KES 4000" },
{ "name": "3", "value": "2025-06-20" }
]
}
]
}
}Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
destination | string | Yes | Recipient's phone number (local or international format) |
template_id | string | Yes | Template ID from the templates endpoint |
phone_number_id | string | Yes | Your WhatsApp Number phone_number_id. Do not confuse with the id field |
component_list | object | Yes | Template variable values |
media_id | string | No | The media_id returned by Media Uploads. Required when the template has a document, image, or video header |
file_name | string | No | Display filename for document templates. If omitted, SMSLeopard uses the filename captured during media upload |
Component List Structure
The component_list contains template variables:
{
"components": [
{
"component_type": "BODY",
"fields": [
{ "name": "1", "value": "First variable value" },
{ "name": "2", "value": "Second variable value" }
]
}
]
}component_type: It can be"HEADER","BODY", or"BUTTONS"fields: Array of variable replacementsname: For"HEADER"and"BODY", use the template placeholder number as a string ("1","2","3", etc.). For"BUTTONS", use the button placeholder number or an explicit button target such as"button:0"or"quick_reply:1".value: Actual value to replace the placeholder
Button Variables
Templates can include buttons. Use the button array from the templates endpoint to identify each button's zero-based position.
Dynamic URL buttons
If a template has a URL button such as:
{
"type": "URL",
"text": "Pay Now",
"url": "https://pay.example.com/invoices/{{1}}"
}send the dynamic part in a BUTTONS component. The field name should match the placeholder number:
{
"component_type": "BUTTONS",
"fields": [
{ "name": "1", "value": "INV-1001" }
]
}You can also target the button by index:
{
"component_type": "BUTTONS",
"fields": [
{ "name": "button:0", "value": "INV-1001" }
]
}Accepted index-targeted field names include button:0, url:0, quick_reply:1, and the same names with _ or - separators such as button_0 or quick-reply:1.
For URL buttons, provide only the value for the placeholder. Do not send the full URL unless the template placeholder is intended to contain a full URL.
Quick reply button payloads
Quick reply buttons do not change the visible button text. The value you provide becomes the payload returned in webhook responses when the recipient taps the button.
{
"component_type": "BUTTONS",
"fields": [
{ "name": "quick_reply:1", "value": "confirm-inv-1001" }
]
}quick_reply:1 means "target the second button in the template's button array as a quick reply button."
Phone number buttons
Meta does not support changing a phone number button at send time. If you need a different phone number, create and approve a separate template with that phone number.
Media Header Templates
If your approved template has a document, image, or video header, upload the file first using Media Uploads, then include the returned media_id when sending the template.
{
"destination": "254720000000",
"template_id": "document_template_id",
"phone_number_id": "46646XXXXXXX268",
"media_id": "987654321098765",
"file_name": "estimate.pdf",
"component_list": {
"components": [
{
"component_type": "BODY",
"fields": [
{ "name": "1", "value": "Peter" },
{ "name": "2", "value": "INV-1001" },
{ "name": "3", "value": "KES 4,000" }
]
}
]
}
}The media type must match the template header. Use document media for document templates, image media for image templates, and video media for video templates.
Response
{
"recipients": [
{
"id": "bae1d2dd-f27d-4d2b-96d9-e907e12b0ba2",
"cost": 0.8,
"number": "+254725089232",
"status": "Success"
}
]
}Response Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique message ID for tracking |
cost | number | Message cost in your account currency |
number | string | Recipient number in international format |
status | string | Delivery status (Success, Failed, etc.) |
Complete Examples
Authentication Code Example
Using the authentication template from the templates section:
Template:
*{{1}}* is your verification code. For your security, do not share this code.Request:
{
"destination": "254720000000",
"template_id": "1250589476699956",
"phone_number_id": "46646XXXXXXX268",
"component_list": {
"components": [
{
"component_type": "BUTTONS",
"fields": [{ "name": "1", "value": "123456" }]
}
]
}
}Sent Message:
*123456* is your verification code. For your security, do not share this code.Order Confirmation Example
Template:
Hi {{1}}, your order #{{2}} worth {{3}} is confirmed for delivery on {{4}}.Request:
{
"destination": "254720000000",
"template_id": "template_order_confirmation_id",
"phone_number_id": "46646XXXXXXX268",
"component_list": {
"components": [
{
"component_type": "BODY",
"fields": [
{ "name": "1", "value": "John Doe" },
{ "name": "2", "value": "ORD-2025-001" },
{ "name": "3", "value": "$99.99" },
{ "name": "4", "value": "June 28, 2025" }
]
}
]
}
}Dynamic URL Button Example
Template body:
Hello {{1}}, your invoice is ready.Template button:
{
"type": "URL",
"text": "Pay Now",
"url": "https://pay.example.com/invoices/{{1}}"
}Request:
{
"destination": "254720000000",
"template_id": "template_with_payment_link",
"phone_number_id": "46646XXXXXXX268",
"component_list": {
"components": [
{
"component_type": "BODY",
"fields": [
{ "name": "1", "value": "Peter" }
]
},
{
"component_type": "BUTTONS",
"fields": [
{ "name": "1", "value": "INV-1001" }
]
}
]
}
}Quick Reply Payload Example
{
"destination": "254720000000",
"template_id": "template_with_quick_reply",
"phone_number_id": "46646XXXXXXX268",
"component_list": {
"components": [
{
"component_type": "BODY",
"fields": [
{ "name": "1", "value": "Peter" }
]
},
{
"component_type": "BUTTONS",
"fields": [
{ "name": "quick_reply:0", "value": "confirm-peter-1001" }
]
}
]
}
}Code Examples
curl -X POST https://whatsapp.smsleopard.com/v1/whatsapp/send \
-H "Authorization: Basic $(echo -n 'api_key:api_secret' | base64)" \
-H "Content-Type: application/json" \
-d '{
"destination": "254720000000",
"template_id": "1250589476699956",
"phone_number_id": "46646XXXXXXX268",
"component_list": {
"components": [
{
"component_type": "BODY",
"fields": [
{
"name": "1",
"value": "123456"
}
]
}
]
}
}'Phone Number Formats
The API accepts phone numbers in International format:
- International format:
+254720000000 - Without plus:
254720000000
Error Response Shape
The API returns a consistent error response shape for all errors:
{
"error_code": string, // e.g., "bad_request", "invalid_format", "unauthorized"
"error_message": string, // actual error message describing the issue
"status_code": integer // e.g., 400, 401, 404, 500
}Best Practices
- Validate phone numbers before sending
- Check template variables - ensure all required variables are provided
- Store message IDs for tracking
- Monitor delivery status using the returned message ID
Message Tracking
Each sent message returns a unique id that you can use for:
- Tracking delivery status
- Customer support queries
- Message analytics
- Debugging failed messages