WhatsApp API
Sending Messages

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/send

Request 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

FieldTypeRequiredDescription
destinationstringYesRecipient's phone number (local or international format)
template_idstringYesTemplate ID from the templates endpoint
phone_number_idstringYesYour WhatsApp Number phone_number_id. Do not confuse with the id field
component_listobjectYesTemplate variable values
media_idstringNoThe media_id returned by Media Uploads. Required when the template has a document, image, or video header
file_namestringNoDisplay 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 replacements
  • name: 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

FieldTypeDescription
idstringUnique message ID for tracking
costnumberMessage cost in your account currency
numberstringRecipient number in international format
statusstringDelivery 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

  1. Validate phone numbers before sending
  2. Check template variables - ensure all required variables are provided
  3. Store message IDs for tracking
  4. 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

Next Steps