Send Message
Send a WhatsApp message to a customer — either an approved template (works any time) or a free-form message (only inside the 24-hour customer-service window).
Requires the x-api-key header. See Overview for details.
HTTP Request
POST /api/dragonfly/whatsapp/messages
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
recipient | string | Yes | Customer's WhatsApp number in international format — +<cc><number> or bare country-code-prefixed digits (e.g. 917545991999). Bare 10-digit national numbers are validated against your organisation's country. |
type | string | Yes | Message type. template, text, image, video, audio, document, location, interactive, contacts, or reaction (case-insensitive). |
templateName | string | For template | Name of the approved template. |
language | string | For template | Template language code. Must exactly match the template's language (en ≠ en_US) — copy it from List Templates. |
components | object[] | For template | Template variable values (header / body / button parameters), in Meta's components format. Pass [] for templates with no variables. |
message | object | For non-template types | The message payload, e.g. { "body": "hi" } for text. |
context | object | No | Reply-to context ({ "message_id": "wamid..." }) to send as a threaded reply. |
Examples
Free-form text message
Only works within 24 hours of the customer's last reply — see the 24-hour window.
{
"recipient": "917093245428",
"type": "text",
"message": { "body": "hi" }
}
Template with no variables
{
"recipient": "917545991999",
"type": "template",
"templateName": "hello_world",
"language": "en_US",
"components": []
}
Template with text variables in header & body
Body variables fill {{1}}, {{2}}, … in order; the header takes a single variable.
{
"recipient": "917545991999",
"type": "template",
"templateName": "waiting_request",
"language": "en",
"components": [
{ "type": "header", "parameters": [{ "type": "text", "text": "H1" }] },
{
"type": "body",
"parameters": [
{ "type": "text", "text": "B1" },
{ "type": "text", "text": "B2" }
]
}
]
}
Template with media in the header
Use parameter type image, video, or document with a public HTTPS link:
{
"recipient": "917545991999",
"type": "template",
"templateName": "video_in_header",
"language": "en",
"components": [
{
"type": "header",
"parameters": [
{ "type": "video", "video": { "link": "https://cdn.example.com/clip.mp4" } }
]
}
]
}
For a document: { "type": "document", "document": { "link": "https://…/file.pdf" } }.
For an image: { "type": "image", "image": { "link": "https://…/pic.png" } }.
Template with a dynamic-URL button
The URL variable is supplied via a button component with sub_type: "url" and the
button's index (0-based):
{
"recipient": "917545991999",
"type": "template",
"templateName": "dynamic_urll",
"language": "en",
"components": [
{
"type": "button",
"sub_type": "url",
"index": 0,
"parameters": [{ "type": "text", "text": "customer_first_name" }]
}
]
}
Sending a dynamic-URL template with empty components fails — the button variable must
always be provided.
Try it
Code Examples
- cURL
- JavaScript
- Python
curl -X POST "https://prod-api.superfone.co.in/superfone/api/dragonfly/whatsapp/messages" \
-H "x-api-key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"recipient": "917545991999",
"type": "template",
"templateName": "hello_world",
"language": "en_US",
"components": []
}'
const response = await fetch(
'https://prod-api.superfone.co.in/superfone/api/dragonfly/whatsapp/messages',
{
method: 'POST',
headers: {
'x-api-key': process.env.SF_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
recipient: '917545991999',
type: 'template',
templateName: 'hello_world',
language: 'en_US',
components: []
})
}
);
const result = await response.json();
if (result.data?.messages?.[0]?.id) {
console.log('Sent:', result.data.messages[0].id);
} else if (result.reason === 'INSUFFICIENT_BALANCE') {
console.warn('Not sent — insufficient wallet balance');
}
import os
import requests
url = 'https://prod-api.superfone.co.in/superfone/api/dragonfly/whatsapp/messages'
headers = {'x-api-key': os.environ['SF_API_KEY']}
payload = {
'recipient': '917545991999',
'type': 'template',
'templateName': 'hello_world',
'language': 'en_US',
'components': [],
}
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()
result = response.json()
message_id = (result.get('data') or {}).get('messages', [{}])[0].get('id')
if message_id:
print('Sent:', message_id)
elif result.get('reason') == 'INSUFFICIENT_BALANCE':
print('Not sent - insufficient wallet balance')
Success Response
Status Code: 200 OK
{
"data": {
"messaging_product": "whatsapp",
"contacts": [
{ "input": "917545991999", "wa_id": "917545991999" }
],
"messages": [
{
"id": "wamid.HBgMOTE3NTQ1OTkxOTk5FQIAERgSMkMyM0I3NjU2QjdGRDYyN0NGAA==",
"message_status": "accepted"
}
]
},
"message": "success"
}
Save data.messages[0].id (the wamid) — you need it to
look up delivery status.
A 200 with a wamid means Meta accepted the message. Delivery, read, and failure
happen asynchronously — check via Get Message by ID or a
WhatsApp events webhook.
Insufficient balance
If your wallet balance cannot cover the send, the message is not sent but the API
still returns HTTP 200 with this body instead of the Meta payload:
{
"message": "Insufficient wallet balance for sending message",
"reason": "INSUFFICIENT_BALANCE"
}
Never treat HTTP 200 alone as a successful send. A send succeeded only if
data.messages[0].id is present.
Error Responses
| Status | Message | When it occurs |
|---|---|---|
400 | Template not found | templateName doesn't exist or language doesn't exactly match the template's language code |
400 | Invalid Message Type | type is not one of the supported values |
400 | Invalid Request | No WhatsApp account is connected to your organisation |
400 | (Meta error message) | Meta rejected the send synchronously (bad recipient, template paused, etc.) |
401 | UnAuthorized, Please Provide Valid API Key | Missing or invalid x-api-key |
Related Endpoints
- Get Message by ID — check delivery status of a sent message
- List Templates — find template names and language codes