Skip to content
ONPADDocs

Docs / API

Send a message

POST /v1/messages — queue a template or free-text WhatsApp message for delivery.

View as Markdown

Queues a WhatsApp message for delivery and returns immediately with an id to track it.

POST https://app.onpad.in/api/v1/messages

Headers

HeaderRequiredValue
AuthorizationYesBearer wh_live_...
Content-TypeYesapplication/json
Idempotency-KeyYesA unique key for this send, 8–120 characters

Idempotency-Key is required, not optional. Without it the request is rejected with 422. It is what stops a retried request from sending the same message twice — see Idempotency.

Allowed characters in the key: letters, digits, ., _, : and -.

Body

Common fields

FieldTypeRequiredDescription
tostringYesRecipient in full international form, digits only, 8–15 digits. Separators are stripped, so +91 98765 43210 is accepted
typestringNotemplate (default) or text

When type is template

FieldTypeRequiredDescription
template.namestringYesThe approved template's name. Lowercase letters, digits and underscores, up to 512 characters
template.languagestringNoLanguage code, default en_US. Must match the approved template
template.parametersarrayYes, if the template has variablesValues for {{1}}, {{2}} … in order. Maximum 20, each 1–1024 characters, text or number

The count must match the template exactly. A template with {{1}} and {{2}} needs two parameters — one or three is a 422.

Templates with an image, video, document or location header are not supported by this endpoint yet. Use a campaign for those.

When type is text

FieldTypeRequiredDescription
text.bodystringYesThe message, 1–4096 characters. body at the top level also works

Free text is only allowed inside the 24-hour customer service window — that is, when the recipient has messaged you in the last 24 hours. Outside it, the request is rejected with 422 and you must send a template instead.

Example request

curl -X POST https://app.onpad.in/api/v1/messages \
  -H "Authorization: Bearer wh_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-10482-shipped" \
  -d '{
    "to": "919876543210",
    "type": "template",
    "template": {
      "name": "order_shipped",
      "language": "en_US",
      "parameters": ["Priya", "#10482", "Friday"]
    }
  }'

919876543210 throughout these docs is a placeholder, not a test number. There is no reserved range in India — whoever owns a number receives whatever you send, and Meta charges you for it. Replace it with your own number before running anything.

Response

202 Accepted when the message is queued:

{
  "ok": true,
  "duplicate": false,
  "message": {
    "id": "6f1b9a2c-4d7e-4a31-9f08-2b5c7d1e3a40",
    "to": "+919876543210",
    "type": "template",
    "status": "queued",
    "meta_message_id": null,
    "attempts": 0,
    "error": null,
    "created_at": "2026-10-04T09:21:33+05:30",
    "sent_at": null,
    "delivered_at": null,
    "read_at": null,
    "status_url": "https://app.onpad.in/api/v1/messages/6f1b9a2c-4d7e-4a31-9f08-2b5c7d1e3a40"
  }
}

200 OK with "duplicate": true when this Idempotency-Key was already used for an identical request. The original message is returned and nothing is sent again.

Fields in the response

FieldDescription
idThe ONPAD message id. Use it with GET /messages
toThe recipient, normalised with a leading +
statusqueued, processing, sent, delivered, read or failed
meta_message_idMeta's own id, available once sent
attemptsHow many delivery attempts have been made
errorThe failure reason, when status is failed
created_at, sent_at, delivered_at, read_atISO 8601 timestamps, null until each happens

202 means accepted, not delivered. Delivery happens in the background — poll for the outcome.

Status codes

CodeMeaning
202Queued
200Duplicate of an earlier identical request
400invalid_json — the body is not valid JSON
401invalid_api_key — missing, malformed, revoked, or the plan does not include API access
403workspace_read_only — the workspace is read-only, usually unpaid billing
405method_not_allowed — only GET and POST are accepted here
409This Idempotency-Key was used for a different request body
422message_rejected — a validation problem; the message field says which
500internal_error — our fault; the message was not queued

Every 422 message is specific. See Errors for the full list and what each one means.

Notes

The recipient becomes a contact. Sending to a new number creates a contact in your workspace, so the conversation appears in the inbox and replies land there.

Sending does not check opt-out. If you maintain an opt-out list, check it before calling the API — ONPAD does not block the send for you.

One message per request. To message many people, loop. There is no bulk endpoint; use a campaign for large sends, which is faster and gives you per-recipient reporting.

Still stuck?

Ask ONPAD, the assistant inside your workspace, can answer questions about your own data — how a specific campaign performed, why one message failed, what your limits are right now.

Ask ONPAD in your workspaceEmail support@onpad.in