WhatsApp API · v1
Get API key

WhatsApp Business API

Send WhatsApp templates, OTPs, chat replies, buttons, lists and bulk campaigns from your own software, and receive delivery reports and customer replies in real time. Built on the official WhatsApp Cloud API (Meta) - SMSGatewayHub is a Meta Tech Provider.

Simple API

Clean JSON: to, template, variables. We build the WhatsApp payload, check opt-outs, billing and the 24-hour rule for you.

Meta-compatible API

Already have code for Meta's Cloud API (or n8n / Make / Pabbly modules)? Send the same JSON to us - get Meta's own reply with the real wamid.

Webhooks

Signed (HMAC-SHA256) events for every status, every customer message, form submissions, payments and AI hand-overs - retried automatically.

Fallback & tracking

Campaigns can fall back to RCS or SMS and track link clicks per recipient - set up in the panel, reported through the same API.

Base URLhttps://www.smsgatewayhub.com/cpaas/whatsapp/api.ashx
FormatJSON in, JSON out (UTF-8). Every action is selected with ?action=.
Success shape{"ok": true, ...} · errors: {"ok": false, "error": "code", "message": "..."} with a matching HTTP status
Time zoneAll times are India Standard Time (IST, UTC+05:30), format yyyy-MM-ddTHH:mm:ss

Quick start

  1. Connect WhatsApp. In the panel go to CPaaS › WhatsApp and connect your number with Facebook (about 5 minutes).
  2. Get an approved template. Templates › New template. Meta usually approves within minutes. Business-initiated messages must use a template.
  3. Create an API key. WhatsApp › API Documentation › Create key. It starts with sghwa_ and is shown once.
  4. Send your first message with the request below, then check its status or set a webhook.

Authentication

Send your API key in the Authorization header as a Bearer token (or in X-API-Key). Keys belong to your panel account; you can have up to 5 active keys and revoke any of them in the panel.

HTTP headers
Authorization: Bearer sghwa_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
Keep keys on your server. Never put an API key in a mobile app, browser JavaScript or a public repository. A missing or wrong key returns 403 unauthorized.

Numbers, limits & idempotency

Phone numbers

Use the full international number, digits only: 919876543210. Spaces, + and dashes are removed; a 10-digit Indian mobile (starting 6-9) gets 91 added automatically.

Rate limit

Up to 50 requests per second per API key. Above that you get 429 rate_limited with Retry-After: 1. For large sends use bulk (1,000 recipients per request). WhatsApp's own throughput and daily limits (by number tier) also apply.

Your reference (ref)

Add "ref": "order-1043" (max 100 characters) to any send. It is returned in every status, in webhooks, and you can look a message up by it.

Idempotency (safe retries)

If a network error leaves you unsure whether a request worked, retry it with the same Idempotency-Key header. Within 24 hours the same key returns the original result ("duplicate": true) instead of sending again.

Header
Idempotency-Key: order-1043-shipped

The 24-hour rule & pricing

WhatsApp has two kinds of outgoing messages:

KindWhenHow to sendMeta category
TemplateAny time - starting a conversation, alerts, offers, OTPssend (type template), bulkMarketing, Utility or Authentication
Session (free-form)Only within 24 hours after the customer's last message to youtext / media, buttons, lists, linksService

Check whether the window is open with GET contact (windowOpen, windowClosesAt). Sending a session message after the window closed returns 409 window_closed.

Each accepted message is charged from your WhatsApp wallet at your price for its category (or covered by your monthly plan). If WhatsApp does not deliver a message, or Meta does not bill it, the amount is returned automatically. See balance.

Send a template

POST/cpaas/whatsapp/api.ashx?action=send
FieldDescription
torequired
string
Customer number with country code.
templaterequired
string
Approved template name (see templates).
languagedefault en
string
Template language code, e.g. en, hi, en_US.
variables
string[]
Values for body {{1}}, {{2}}... in order. New lines and tabs are flattened (WhatsApp rule).
header
object
{"text": "..."} for a text header with {{1}}, or {"media": "https://...", "filename": "bill.pdf"} for image / video / document headers.
buttons
string[]
Values for dynamic link buttons (the part after the fixed URL), in order.
ref
string
Your reference, max 100.

The response is immediate: the message is queued and normally reaches WhatsApp within 1-2 seconds. Opted-out numbers are refused (409 opted_out).

Response 200
{"ok": true, "messageId": 1045, "status": "queued", "ref": "order-1043"}

Send an OTP

Create an Authentication template in the panel (Templates › New template › Authentication). Meta fixes the wording ("123456 is your verification code") and adds a Copy code button. Send the code as the only variable:

Authentication messages are delivered first and priced lower than marketing. Keep codes short-lived and never put OTPs in marketing templates.

Coupon, limited-time offer & carousel templates

Create these in the panel (Templates › Rich templates). Each has default values saved with it - the coupon code, how long the offer runs, and every card's image / video and link value - so a normal send or bulk request needs only the usual variables. To change them for one message, add:

FieldForDescription
coupon
string
Coupon / offerThe code the customer copies (1-15 letters / numbers).
offerExpiresAt
string
Limited-time offerWhen the countdown ends (IST), e.g. 2026-10-31T23:59:00. Default: N hours after sending, as set in the panel.
cards
object[]
CarouselOne object per card, in order: media (https image / video), variables (card text values), button (value for the card link's {{1}}). Missing values use the defaults.
Carousel cards: 2-10, all images or all videos, the same buttons on every card. A quick reply tapped on a card arrives in message.inbound with type button.

Send text & media (session messages)

POST/cpaas/whatsapp/api.ashx?action=send

Within the 24-hour window you can reply freely. Set type and the matching object:

typeBodyLimits
text"text": "Hello" (or {"body": "Hello"})4096 characters, links get a preview
image"media": {"link": "https://...jpg", "caption": "..."}JPG / PNG, 5 MB
video"media": {"link": "https://...mp4", "caption": "..."}MP4 / 3GP, 16 MB
audio"media": {"link": "https://...mp3"}16 MB, no caption
document"media": {"link": "https://...pdf", "filename": "invoice.pdf", "caption": "..."}100 MB

Media links must be public https URLs - or upload the file first with POST media.

Buttons, lists and link buttons

Set "type": "interactive" and an interactive object. When the customer taps a button or list row, you receive a message.inbound webhook with replyId = the id you set.

interactive.typeWhat the customer seesLimits
buttonsUp to 3 reply buttons under the messagetitle 20 chars, id 200 chars
listA menu button opening up to 10 rows in sectionsbutton 20, row title 24, description 72
cta_urlOne button that opens a websitebutton text 20

Optional for all: header (text, 60), footer (60). body is required (1024). Ids must be unique and must not look like p12 or b1.x.0 (reserved for our chatbot and catalog).

Bulk send / campaigns

POST/cpaas/whatsapp/api.ashx?action=bulk

Send one template to up to 1,000 recipients per request, each with their own variables. The batch becomes a campaign you can watch live in the panel (delivered, read, clicked, replied, failure reasons, retarget). Invalid, duplicate and opted-out numbers are reported in rejected and the rest are sent.

FieldDescription
template, languagerequiredAs in send.
recipientsrequired
object[]
Each: to, optional variables, header, buttons, ref.
name
string
Campaign name shown in the panel.
scheduleAt
string
IST date-time within 30 days, e.g. 2026-10-07T10:30:00. Omit to send now.

The wallet must cover all accepted messages up front (402 insufficient_balance otherwise). Larger lists: send several requests, or upload Excel / CSV in the panel (no limit).

Response 200
{
  "ok": true, "campaignId": 88, "status": "queued", "scheduledAt": null,
  "accepted": 2, "rejectedCount": 1,
  "messages": [
    {"index": 0, "to": "919876543210", "messageId": 2101, "ref": "inv-1"},
    {"index": 1, "to": "919812345678", "messageId": 2102, "ref": "inv-2"}
  ],
  "rejected": [{"index": 2, "to": "12345", "error": "bad_number"}]
}

Meta-compatible API

POST/cpaas/whatsapp/api.ashx?action=metaoptional ?phone_number_id=

Send exactly the JSON of Meta's Cloud API POST /{phone-number-id}/messages - any message type Meta supports (template with any components, text, image, video, audio, document, sticker, location, contacts, interactive including flows and catalogs). We check billing, opt-outs (templates) and the 24-hour window (session messages), send it to WhatsApp straight away and return Meta's own response with the real wamid - plus sgh_message_id for our status API.

  • Your reference: Meta's biz_opaque_callback_data is stored as ref and comes back in our webhooks.
  • Errors use Meta's format ({"error": {"message", "code", ...}}) so existing error handling keeps working.
  • Several numbers? Pass ?phone_number_id= (from the panel); default is your default number.
Response 200 (from Meta)
{
  "messaging_product": "whatsapp",
  "contacts": [{"input": "919876543210", "wa_id": "919876543210"}],
  "messages": [{"id": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAERgS..."}],
  "sgh_message_id": 1046
}
Tip for n8n / Make / Pabbly: use their "HTTP request" module with this URL and your sghwa_ key as Bearer token, and paste the Meta JSON body.

Message status

GET/cpaas/whatsapp/api.ashx?action=status&id=1045

Look up by id (our messageId), by ref (your reference - latest match), or up to 100 at once with ids=1045,1046,1047 (returns messages[]). For real-time updates prefer webhooks.

Response 200
{
  "ok": true, "messageId": 1045, "toNumber": "919876543210", "messageType": "template",
  "templateName": "order_update", "campaignId": null, "status": "read",
  "errorCode": null, "errorText": null, "clientRef": "order-1043",
  "createdAt": "2026-10-06T12:01:41", "sentAt": "2026-10-06T12:01:43",
  "deliveredAt": "2026-10-06T12:01:44", "readAt": "2026-10-06T12:05:10",
  "repliedAt": null, "clickedAt": null, "failedAt": null,
  "pricingCategory": "utility", "fallbackStatus": null
}

List messages

GET/cpaas/whatsapp/api.ashx?action=messages&from=2026-10-01&to=2026-10-06
QueryDescription
from, toDates (yyyy-MM-dd), max 31 days. Default: last 7 days.
statusqueued, sending, sent, delivered, read, failed, cancelled
number, campaignIdFilter by recipient or campaign.
page100 per page, newest first; hasMore tells you if there is a next page.

Status lifecycle

queued→sending→sent→delivered→readorfailed
StatusMeaning
queuedAccepted by us, waiting to go to WhatsApp (normally < 2 s; scheduled campaigns wait for their time).
sendingBeing handed to WhatsApp right now.
sentWhatsApp accepted it (one grey tick). sentAt is set.
deliveredOn the customer's phone (two grey ticks).
readOpened (two blue ticks) - only if the customer has read receipts on.
failedNot delivered - see errorCode / errorText and WhatsApp errors. Charges are refunded.
cancelledCampaign cancelled before this message was sent.

Extra timestamps: repliedAt (customer replied), clickedAt (tracked link clicked). Statuses never go backwards.

Templates

GET/cpaas/whatsapp/api.ashx?action=templates

Your approved templates with what each needs: variables (body values), headerType (NONE, TEXT, IMAGE, VIDEO, DOCUMENT), headerVariable, buttonVariables and Meta's raw components. Create and edit templates in the panel (Meta reviews them).

Upload media

POST/cpaas/whatsapp/api.ashx?action=mediamultipart/form-data, field "file"

Returns a public https link you can use in templates (header.media) and session messages (media.link). JPG / PNG up to 5 MB, MP4 / 3GP 16 MB, PDF / Office / TXT 100 MB. The file content is checked against its type.

Response 200
{"ok": true, "link": "https://www.smsgatewayhub.com/cpaas/media/202610/7f3c...e1.pdf", "fileName": "invoice.pdf", "size": 48211}

Contact & 24-hour window

GET/cpaas/whatsapp/api.ashx?action=contact&number=919876543210

Everything we know about a customer: name, tags, attributes (e.g. form answers), assigned agent and department, opt-out and block state - and windowOpen / windowClosesAt to decide between a session message and a template.

Response 200
{
  "ok": true, "number": "919876543210", "known": true, "name": "Ravi Kumar",
  "email": null, "conversationStatus": "open", "assignedTo": "Priya",
  "department": "Sales", "tags": ["lead", "interested"], "attributes": {"city": "Indore"},
  "blocked": false, "optedOut": false,
  "windowOpen": true, "windowClosesAt": "2026-10-07T09:42:23",
  "lastInboundAt": "2026-10-06T09:42:23", "firstSeenAt": "2026-10-05T21:25:36"
}

Opt-outs

GET/cpaas/whatsapp/api.ashx?action=optouts&page=1
POST/cpaas/whatsapp/api.ashx?action=optout{"number": "919876543210", "op": "add" | "remove"}

Customers who reply STOP / UNSUBSCRIBE are added automatically. Templates to opted-out numbers are refused (409 opted_out) and skipped in bulk. Sync your own unsubscribe list with op: add. 500 per page.

Balance

GET/cpaas/whatsapp/api.ashx?action=balance
Response 200
{"ok": true, "billingActive": true, "mode": "per_message", "currency": "INR", "balance": 2450.75, "planName": null, "planValidTill": null}

mode: per_message (charged per message from balance), plan (monthly plan, valid till planValidTill) or free.

Webhooks: setup & security

In the panel open WhatsApp › Automation › Webhooks, enter your https URL, choose the events and save. You get a signing secret once. We POST JSON to your URL; reply with any 2xx within 10 seconds. Failed deliveries are retried 6 times with growing gaps (2, 4, 8, 16, 32, 64 minutes). Use Send test to receive a test.ping.

HeaderValue
X-SGH-EventEvent name, e.g. message.status
X-SGH-Signaturesha256= + hex HMAC-SHA256 of the raw request body using your signing secret

Always verify the signature before trusting the data, compare in constant time, and process events idempotently (the same event can arrive twice after a retry).

Events & payloads

EventWhen
message.statusA message you sent changes status (sent, delivered, read, failed).
message.inboundA customer sends you a message, taps a button or picks a list row (replyId).
flow.submittedA customer submits a WhatsApp form (Flow) - all answers in fields.
payment.paidA payment request sent from WhatsApp is paid.
ai.handoffThe AI assistant hands a chat to your team.
test.pingYou pressed "Send test" in the panel.
message.status
{
  "event": "message.status", "timestamp": "2026-10-06T12:01:44",
  "data": {
    "messageId": 1045, "to": "919876543210", "ref": "order-1043",
    "campaignId": null, "status": "delivered", "at": "2026-10-06T12:01:44",
    "errorCode": null, "errorText": null,
    "wamid": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAERgS..."
  }
}
flow.submitted
{
  "event": "flow.submitted", "timestamp": "2026-10-06T12:20:05",
  "data": {"contactId": 77, "from": "919876543210", "flowId": 4,
           "fields": {"name": "Ravi", "email": "ravi@example.com", "interested_in": "WhatsApp API"}}
}
message.inbound
{
  "event": "message.inbound", "timestamp": "2026-10-06T12:10:02",
  "data": {
    "from": "919876543210", "name": "Ravi", "type": "interactive",
    "text": "Looks good", "replyId": "yes", "mediaId": null,
    "wamid": "wamid.HBgMOTE5...", "contactId": 77,
    "receivedAt": "2026-10-06T12:10:01", "adReferral": null
  }
}
payment.paid · ai.handoff
{"event": "payment.paid", "timestamp": "...",
 "data": {"payId": 31, "number": "919876543210", "amount": 999.00, "note": "Order #1043"}}

{"event": "ai.handoff", "timestamp": "...",
 "data": {"contactId": 77, "from": "919876543210", "question": "I want a refund"}}

type in message.inbound: text, image, video, audio, document, sticker, location, contacts, button, interactive. Media can be viewed in the panel inbox.

Error codes

HTTPerrorMeaning / what to do
400bad_jsonBody is not valid JSON or too large (64 KB; bulk 4 MB).
400bad_numberNumber is not 8-15 digits with country code.
400variables / header / buttonsThe template needs more (non-empty) values - the message says how many.
400text / media / interactive / bad_typeSession message fields missing or over the limits.
400recipients / schedule / no_valid_recipientsBulk request problems.
400bad_ref / bad_idempotency_keyref over 100 characters / Idempotency-Key format.
402insufficient_balanceRecharge your WhatsApp wallet.
402plan_expired / no_rateRenew your plan / your price is not set - contact your account manager.
403unauthorizedMissing, wrong or revoked key, or WhatsApp not enabled for the account.
404template_not_found / not_foundTemplate name + language not found (sync in the panel) / unknown message or action.
409template_not_approvedTemplate is pending, rejected or paused.
409window_closedNo customer message in the last 24 hours - send a template.
409opted_out / blockedCustomer unsubscribed / you blocked them.
409not_connected / no_numberConnect WhatsApp / register a number in the panel.
413too_largeMedia file over its size limit.
429rate_limitedMore than 50 requests per second - slow down (Retry-After: 1).
500server_errorTemporary problem - retry with the same Idempotency-Key.

WhatsApp (Meta) errors

When WhatsApp itself refuses or fails a message, its code appears in errorCode / errorText (status API and webhooks). The common ones:

CodeMeaningWhat to do
131026Message undeliverableThe number is not on WhatsApp, or uses an old app version, or has not accepted the new terms. Use SMS fallback.
131047Re-engagement messageMore than 24 hours since the customer's last message - send a template.
131049Not delivered to keep the ecosystem healthyMeta limits how many marketing messages one person gets. Retry later, improve relevance.
131050User stopped marketing messagesThe customer turned off your marketing messages in WhatsApp.
131051Unsupported message typeCheck the message type / object.
131056Pair rate limitToo many messages to the same number in a short time - slow down.
130429Throughput limitWe retry automatically.
131042Payment issueThe WhatsApp Business Account has a billing problem in Meta (card / credit line).
132000 / 132001Template variable count / template does not existCheck templates and language.
132015 / 132016Template paused / disabledQuality was low - edit or create a new template.
368Temporarily blocked for policy violationsCheck Account Quality in WhatsApp Manager.

Best practices & go-live checklist

  • Use webhooks, not polling, for statuses and replies. Verify the signature; answer 2xx fast and process in the background.
  • Store our messageId and your ref together; use Idempotency-Key on every send you might retry.
  • Only message people who opted in, honour STOP immediately (we do it for you), and keep marketing relevant - quality rating decides your daily limit.
  • Check windowOpen before session messages; outside the window always use a template.
  • OTPs: Authentication template, code as the only variable, short expiry.
  • Large sends: bulk in batches of 1,000 or a scheduled campaign; watch the campaign page for failure reasons and retarget non-readers.
  • Media: host on fast https or use upload; keep images under 1 MB for quick delivery.
  • Security: keys only on servers, one key per system, revoke unused keys.

Postman & OpenAPI

Postman collection

All requests ready to run - set the apiKey variable. Download

OpenAPI 3 spec

Generate clients in any language or import into your API tools. Download

Need help?

support@smsgatewayhub.com · your account manager. Include the messageId and the time.

Changelog

2026-10-06v1.1: coupon code, limited-time offer and carousel templates (coupon, offerExpiresAt, cards).
2026-10-06v1: session messages (text, media, buttons, lists, link buttons), bulk / scheduled campaigns, Meta-compatible endpoint, status for many ids, message list, contact & 24-hour window, opt-outs, balance, media upload, Idempotency-Key, webhook events flow.submitted / payment.paid / ai.handoff and replyId.
2026-10-05Initial release: send template, status, templates; signed webhooks.