WA Gateway API Dashboard

API reference

Send WhatsApp messages with a single HTTP request. Messages are accepted immediately, queued, and delivered as soon as capacity allows โ€” your request never blocks waiting on delivery.

Base URL

https://smsgateway.swiftcoder.in/api/v1

All paths below are relative to this. Requests and responses are JSON; send Content-Type: application/json on anything with a body.

Authentication

Every request carries an API key. Pass it as a bearer token:

Authorization: Bearer sk_a1b2c3d4_your_key_here

Or, if a bearer header is inconvenient, as a dedicated header:

X-API-Key: sk_a1b2c3d4_your_key_here
Treat a key as write-once. It is shown in full when issued; if you lose it, ask whoever issued it rather than guessing. Keys carry scopes: messages:send to send, messages:read to read back. A key without the right scope gets 403.

A key is a bearer credential: anyone holding it can send messages at your expense. Keep it server-side โ€” never in a browser, mobile binary, or public repository.

The 24-hour window

WhatsApp does not let a business open a conversation with free text. Which of the two shapes you need depends on one thing:

SituationWhat to send
The recipient messaged you in the last 24 hours Free text โ€” a body
Anything else: a notification, an OTP, an order update An approved template
Most integrations want templates. If you are sending codes, receipts or alerts, you are almost always outside the window, and a body will be rejected. Send a template and the same call works whether the window is open or not.

Templates are written and approved in your WhatsApp Business account before you can use them. This API sends one by name; it does not create or submit them.

Send a message

POST /messages

Parameters

FieldTypeDescription
tostring, required Recipient number in E.164 (+919140327455), country code included โ€” spaces, dashes and brackets are accepted and stripped.
bodystring Free message text. Only reaches someone inside the 24-hour window. Required unless you send a template.
templateobject An approved template to send instead of body โ€” see below.
clientRefstring, optional Your own reference for this message. Makes the request safe to retry โ€” see Idempotency.
senderIdstring, optional Send from one specific number of yours. Omit and any of them may carry it; list them with GET /senders.

Request

curl -X POST https://smsgateway.swiftcoder.in/api/v1/messages \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+919140327455",
    "body": "Thanks โ€” your driver is two minutes away.",
    "clientRef": "trip-8842"
  }'
const res = await fetch("https://smsgateway.swiftcoder.in/api/v1/messages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.WA_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    to: "+919140327455",
    body: "Thanks โ€” your driver is two minutes away.",
    clientRef: "trip-8842",
  }),
});

if (!res.ok) throw new Error(`send failed: ${res.status}`);
const message = await res.json();
console.log(message.id, message.status);
import os, requests

res = requests.post(
    "https://smsgateway.swiftcoder.in/api/v1/messages",
    headers={"Authorization": f"Bearer {os.environ['WA_API_KEY']}"},
    json={
        "to": "+919140327455",
        "body": "Thanks โ€” your driver is two minutes away.",
        "clientRef": "trip-8842",
    },
    timeout=10,
)
res.raise_for_status()
message = res.json()
print(message["id"], message["status"])
$ch = curl_init("https://smsgateway.swiftcoder.in/api/v1/messages");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("WA_API_KEY"),
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "to" => "+919140327455",
        "body" => "Thanks โ€” your driver is two minutes away.",
        "clientRef" => "trip-8842",
    ]),
]);
$message = json_decode(curl_exec($ch), true);
echo $message["id"], " ", $message["status"];

Response 202 Accepted

{
  "id": "6a9720e14e2f3b1b37d311d6",
  "to": "+919140327455",
  "body": "Thanks โ€” your driver is two minutes away.",
  "template": null,
  "channel": "whatsapp",
  "status": "queued",
  "clientRef": "trip-8842",
  "senderId": null,
  "providerMessageId": null,
  "attempts": 0,
  "lastError": null,
  "lastErrorCode": null,
  "createdAt": "2026-09-01T19:01:02.627Z",
  "sentAt": null,
  "deliveredAt": null,
  "readAt": null,
  "failedAt": null
}

202 means accepted for delivery, not delivered. Keep the id and poll it if you need the outcome.

Send a template

Replace body with a template object. Send one or the other โ€” both together is a 400.

FieldTypeDescription
template.namestring, required The approved template's name, exactly as it appears in your WhatsApp Business account.
template.languagestring Language code of the approved copy, e.g. en_US. Defaults to en_US.
template.componentsarray, optional Values for the template's placeholders, in WhatsApp's component format. Omit for a template that takes none.
curl -X POST https://smsgateway.swiftcoder.in/api/v1/messages \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+919140327455",
    "clientRef": "signup-8842",
    "template": {
      "name": "verification_code",
      "language": "en_US",
      "components": [
        {
          "type": "body",
          "parameters": [{ "type": "text", "text": "123456" }]
        }
      ]
    }
  }'

The message text comes from the approved template, so the response's body is null and template names what was sent. A template that is not approved, or whose parameters do not match the approved copy, fails with the reason in lastError.

Send in bulk

POST /messages/bulk

Up to 500 messages per request. Each entry takes the same fields as a single send and is validated on its own, so one bad number does not reject the batch.

curl -X POST https://smsgateway.swiftcoder.in/api/v1/messages/bulk \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      { "to": "+919140327455", "template": { "name": "order_shipped" } },
      { "to": "not-a-number",  "template": { "name": "order_shipped" } }
    ]
  }'

Response 207 Multi-Status

{
  "results": [
    { "index": 0, "status": "accepted", "message": { "id": "6a97โ€ฆ", "status": "queued" } },
    { "index": 1, "status": "rejected", "error": "invalid_recipient",
      "message": "`to` must be a phone number in E.164 form (+15551234567), including the country code." }
  ]
}

Always 207, even if every entry succeeded. Read each result by its index, which matches the order you sent.

Check a message

GET /messages/:id

Returns the message with its current state and timestamps.

curl https://smsgateway.swiftcoder.in/api/v1/messages/6a9720e14e2f3b1b37d311d6 \
  -H "Authorization: Bearer $WA_API_KEY"
{
  "id": "6a9720e14e2f3b1b37d311d6",
  "to": "+919140327455",
  "status": "read",
  "attempts": 1,
  "lastError": null,
  "lastErrorCode": null,
  "sentAt": "2026-09-01T19:01:02.627Z",
  "deliveredAt": "2026-09-01T19:01:04.133Z",
  "readAt": "2026-09-01T19:02:11.980Z",
  "failedAt": null
}

A message you did not send with this key returns 404.

List messages

GET /messages

Your key's messages, newest first.

QueryDescription
statusFilter by state, e.g. failed
limit1โ€“200, default 50
beforePass the previous response's nextBefore to page back
curl "https://smsgateway.swiftcoder.in/api/v1/messages?status=failed&limit=20" \
  -H "Authorization: Bearer $WA_API_KEY"
{
  "data": [ { "id": "6a97โ€ฆ", "to": "+919140327455", "status": "failed" } ],
  "nextBefore": "6a97โ€ฆ"
}

nextBefore is null on the last page. Paging is by cursor, so new messages arriving mid-scan will not shift your results.

Your numbers

GET /senders

The numbers your messages can go out from. Use an id from here as senderId to pin a message to one of them.

{
  "data": [
    { "id": "6a97โ€ฆ", "name": "Support line", "phoneNumber": "+15551234567",
      "channels": ["whatsapp"], "lastUsedAt": "2026-09-01T19:01:02.627Z" }
  ]
}

Delivery states

A message moves forward through these states and never backwards.

StateMeaning
queued Accepted and waiting for capacity.
dispatched Being sent right now.
sent WhatsApp has taken the message.
delivered It reached the recipient's phone.
read They opened it. Only reported if they have read receipts on.
failed Terminal. lastError says why, and lastErrorCode is WhatsApp's own numeric code.
Treat delivered as success. read depends on a setting the recipient controls, so plenty of messages that were read never report it. A message resting at sent usually means the recipient's phone has not been online since.

Transient failures โ€” a rate limit, a moment when WhatsApp is unreachable โ€” are retried automatically up to 3 times, spaced out so each attempt gets a fresh chance. A permanent refusal, such as a number that is not on WhatsApp, fails immediately rather than burning retries on an answer that will not change. You do not need to implement retries yourself.

Idempotency

Network timeouts leave you unsure whether a message was accepted. Resending blindly risks a duplicate. Send a clientRef โ€” any string unique to that message in your own system โ€” and a repeat of the same request returns the original message instead of sending a second one.

{ "to": "+919140327455", "template": { "name": "verification_code" }, "clientRef": "signup-8842" }

Scoped to your API key, so your references cannot collide with anyone else's. The response is the first message โ€” including its original content โ€” so changed content under a reused reference is not sent.

Errors

Failures return the matching HTTP status and a JSON body with a stable error code and a human-readable message. Branch on the code, not the text.

{ "error": "invalid_recipient", "message": "`to` must be a phone number in E.164 form (+15551234567), including the country code." }
StatusCodeMeaning
400invalid_recipientMissing or unparseable to
400invalid_bodyNeither body nor template, or both at once
400invalid_templateMalformed template name, language or components
400invalid_senderUnknown senderId
400invalid_channelUnknown channel requested
400too_many_messagesMore than 500 in one bulk request
401missing_api_keyNo key on the request
401invalid_api_keyUnknown or revoked key
403insufficient_scopeKey lacks the required scope
404not_foundNo such message under this key
410channel_unavailableThe retired sms channel

A 4xx will not succeed on retry โ€” fix the request. A 5xx is safe to retry, and safest with a clientRef.

Health

GET /health

Unauthenticated liveness check, suitable for a monitor.

curl https://smsgateway.swiftcoder.in/api/v1/health
{ "ok": true, "uptime": 1284.51 }