Overview
SMSTAKE WhatsApp Business messaging is an HTTPS JSON API at https://api.smstake.com/v1/whatsapp/. Business-initiated messages use a template Meta has approved: utility, authentication, or marketing. Images, documents, reply buttons and call-to-action URLs are session messages. Send those after the customer has messaged your WhatsApp number, inside the customer-care window. Category rates are covered in the WhatsApp pricing guide. The phone number and token are issued after onboarding.
| API | Method | Endpoint |
|---|---|---|
| Send a WhatsApp message | POST | https://api.smstake.com/v1/whatsapp/send |
Related guides: WhatsApp Business API, SMS API, RCS API, and the SMSTAKE blog.
Authentication
Send a Bearer token on every call to https://api.smstake.com/v1/whatsapp/:
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
- Use HTTPS only.
- Store the token in an environment variable. Do not commit it to source control.
- Rotate it from the SMSTAKE account, or contact support if it is exposed.
Send a template message
Endpoint: POST https://api.smstake.com/v1/whatsapp/send with type set to template. The template name, language and body must match the version approved in Meta.
| Field | Required | Description |
|---|---|---|
to | Yes | Recipient number in E.164, for example +919876543210 |
type | Yes | template for an approved template. image, document or interactive for a session message |
template.name | For a template | Approved template name |
template.language | For a template | Language code on that template, such as en or hi |
template.category | For a template | utility, authentication or marketing |
template.components | When the template has variables | Body, header or button parameters, in the order the template defines |
client_ref | No | Your reference. It is echoed on the response and on status webhooks |
| category | Use it for |
|---|---|
utility | Order updates, shipping alerts, account notices |
authentication | One-time passcodes |
marketing | Offers, product launches, re-engagement. The customer must have opted in |
Utility template
curl -X POST "https://api.smstake.com/v1/whatsapp/send" \
-H "Authorization: Bearer $SMSTAKE_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"to": "+919876543210",
"type": "template",
"client_ref": "order_ORD2847",
"template": {
"name": "order_shipped",
"language": "en",
"category": "utility",
"components": [
{ "type": "body", "parameters": [{ "type": "text", "text": "ORD-2847" }] }
]
}
}'
import os, requests
resp = requests.post(
"https://api.smstake.com/v1/whatsapp/send",
headers={"Authorization": f"Bearer {os.environ['SMSTAKE_API_TOKEN']}"},
json={
"to": "+919876543210",
"type": "template",
"client_ref": "order_ORD2847",
"template": {
"name": "order_shipped",
"language": "en",
"category": "utility",
"components": [
{"type": "body", "parameters": [{"type": "text", "text": "ORD-2847"}]},
],
},
},
timeout=30,
)
print(resp.json())
const res = await fetch("https://api.smstake.com/v1/whatsapp/send", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SMSTAKE_API_TOKEN}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
to: "+919876543210",
type: "template",
client_ref: "order_ORD2847",
template: {
name: "order_shipped",
language: "en",
category: "utility",
components: [
{ type: "body", parameters: [{ type: "text", text: "ORD-2847" }] }
]
}
})
});
console.log(await res.json());
Authentication and marketing
Change category, name and the body parameters. An authentication template carries the one-time code. A marketing template is only for customers who opted in.
{
"template": {
"name": "login_otp",
"language": "en",
"category": "authentication",
"components": [
{ "type": "body", "parameters": [{ "type": "text", "text": "482913" }] }
]
}
}
{
"template": {
"name": "festive_offer",
"language": "hi",
"category": "marketing",
"components": [
{ "type": "body", "parameters": [{ "type": "text", "text": "Ananya" }] }
]
}
}
Success response
Store message_id so you can match a later delivery or read webhook.
{
"status": "accepted",
"message_id": "msg_8f3k2j9x1m",
"channel": "whatsapp",
"to": "+919876543210",
"client_ref": "order_ORD2847"
}
Images and documents
Session messages use the same endpoint. link must be a public HTTPS URL. Send these after the customer has written to your number.
{
"to": "+919876543210",
"type": "image",
"client_ref": "invoice_preview_1001",
"image": {
"link": "https://cdn.example.com/invoice-preview.jpg",
"caption": "Your invoice preview"
}
}
{
"to": "+919876543210",
"type": "document",
"client_ref": "invoice_pdf_1001",
"document": {
"link": "https://cdn.example.com/invoice-1001.pdf",
"filename": "invoice-1001.pdf",
"caption": "Invoice for order ORD-2847"
}
}
curl -X POST "https://api.smstake.com/v1/whatsapp/send" \
-H "Authorization: Bearer $SMSTAKE_API_TOKEN" \
-H "Content-Type: application/json" \
--data-binary @document.json
import os, requests
resp = requests.post(
"https://api.smstake.com/v1/whatsapp/send",
headers={"Authorization": f"Bearer {os.environ['SMSTAKE_API_TOKEN']}"},
json={
"to": "+919876543210",
"type": "image",
"image": {"link": "https://cdn.example.com/invoice-preview.jpg", "caption": "Your invoice preview"},
},
timeout=30,
)
print(resp.json())
const res = await fetch("https://api.smstake.com/v1/whatsapp/send", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SMSTAKE_API_TOKEN}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
to: "+919876543210",
type: "document",
document: {
link: "https://cdn.example.com/invoice-1001.pdf",
filename: "invoice-1001.pdf",
caption: "Invoice for order ORD-2847"
}
})
});
console.log(await res.json());
Reply buttons and CTA URLs
Interactive messages are also session messages on POST https://api.smstake.com/v1/whatsapp/send. A reply-button message can include up to three buttons. A call-to-action URL opens one HTTPS link.
Reply buttons
{
"to": "+919876543210",
"type": "interactive",
"client_ref": "support_menu_1001",
"interactive": {
"type": "button",
"body": { "text": "How can we help with order ORD-2847?" },
"action": {
"buttons": [
{ "type": "reply", "reply": { "id": "track", "title": "Track order" } },
{ "type": "reply", "reply": { "id": "support", "title": "Talk to support" } }
]
}
}
}
Call-to-action URL
{
"to": "+919876543210",
"type": "interactive",
"client_ref": "track_ORD2847",
"interactive": {
"type": "cta_url",
"body": { "text": "Your order ORD-2847 is out for delivery." },
"action": {
"name": "cta_url",
"parameters": {
"display_text": "Track order",
"url": "https://example.com/track/ORD-2847"
}
}
}
}
curl -X POST "https://api.smstake.com/v1/whatsapp/send" \
-H "Authorization: Bearer $SMSTAKE_API_TOKEN" \
-H "Content-Type: application/json" \
--data-binary @interactive.json
import json, os, requests
payload = json.load(open("interactive.json", encoding="utf-8"))
resp = requests.post(
"https://api.smstake.com/v1/whatsapp/send",
headers={"Authorization": f"Bearer {os.environ['SMSTAKE_API_TOKEN']}"},
json=payload,
timeout=30,
)
print(resp.json())
import { readFile } from "node:fs/promises";
const payload = JSON.parse(await readFile("interactive.json", "utf8"));
const res = await fetch("https://api.smstake.com/v1/whatsapp/send", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SMSTAKE_API_TOKEN}`,
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
console.log(await res.json());
Incoming and status webhooks
Register an HTTPS URL on the SMSTAKE account. SMSTAKE posts delivery, read and incoming-message events as JSON. Return 200 OK as soon as the event is stored. Verify X-SMSTAKE-Signature against the raw body before you trust the payload. The value is sha256= plus the hex HMAC-SHA256 of those exact bytes. Deduplicate on message_id plus event. Receiver patterns are in the DLR webhook integration guide.
| Event | When it is sent |
|---|---|
message.delivered | The message reached the customer's WhatsApp |
message.read | The customer opened the message. Read receipts depend on their settings |
message.received | The customer sent a text, or tapped a reply button |
message.delivered
POST https://yourapp.com/webhooks/smstake/whatsapp
Content-Type: application/json
X-SMSTAKE-Signature: sha256=<hex-encoded HMAC-SHA256 of the raw body>
{
"event": "message.delivered",
"message_id": "msg_8f3k2j9x1m",
"channel": "whatsapp",
"to": "+919876543210",
"status": "delivered",
"client_ref": "order_ORD2847",
"occurred_at": "2026-10-21T14:15:08+05:30"
}
message.read
{
"event": "message.read",
"message_id": "msg_8f3k2j9x1m",
"channel": "whatsapp",
"to": "+919876543210",
"status": "read",
"client_ref": "order_ORD2847",
"occurred_at": "2026-10-21T14:16:11+05:30"
}
message.received
A text reply uses type: "text". A reply-button tap uses type: "button_reply" and includes the button id you set on send.
{
"event": "message.received",
"message_id": "wamid_in_1001",
"channel": "whatsapp",
"from": "+919876543210",
"type": "text",
"text": "Where is my order?",
"occurred_at": "2026-10-21T14:17:02+05:30"
}
{
"event": "message.received",
"message_id": "wamid_in_1002",
"channel": "whatsapp",
"from": "+919876543210",
"type": "button_reply",
"button": { "id": "track", "title": "Track order" },
"occurred_at": "2026-10-21T14:17:40+05:30"
}
HTTP status
| HTTP status | Meaning |
|---|---|
| 200 / 202 | Message accepted for sending |
| 400 | Invalid JSON, or a required template or media field is missing |
| 401 | Missing or invalid Bearer token |
| 403 | Token is valid but not allowed for this WhatsApp number or action |
| 429 | Rate limit exceeded. Retry with backoff |
| 5xx | Temporary server or upstream issue. Retry with backoff |