API Reference · v1
WapiSnap REST API
Send WhatsApp messages from any system over plain HTTP. Per-workspace API keys, JSON request/response, async send with status polling. Base URL:https://api.wapisnap.com.
Authentication
Every request to /v1/* must include an Authorization: Bearer header. Keys are scoped to a single workspace and are issued from Settings → API Keys in the dashboard. The plaintext value appears only at creation time; we store a bcrypt hash + the last 8 chars for display.
Authorization: Bearer wapi_live_a1B2c3D4e5F6g7H8i9J0kLmNoPqRsTuVEvery authenticated request lands in the workspace's request log, viewable at Settings → API Keys → View logs. Useful for confirming a call arrived as expected, debugging non-2xx responses, and correlating the request_id from an error response back to its origin.
Send a message
POST /v1/messages — enqueues a send and returns immediately with a message id. The actual Meta call happens out of band; poll GET /v1/messages/{id} for the final status. Two body shapes are supported.
Text message
POST /v1/messages HTTP/1.1
Host: api.wapisnap.com
Authorization: Bearer wapi_live_...
Content-Type: application/json
{
"to": "+919876543210",
"type": "text",
"text": "Your appointment is confirmed for tomorrow at 11 AM."
}Template message
The template must be APPROVED on Meta's side. Variables follow the grouped shape: body, header, button_url. For templates containing a FLOW button, WapiSnap mints the flow_token server-side, you do not pass it.
POST /v1/messages HTTP/1.1
Host: api.wapisnap.com
Authorization: Bearer wapi_live_...
Content-Type: application/json
{
"to": "+919876543210",
"type": "template",
"template": {
"name": "application_followup_new_lead",
"language": "en",
"variables": {
"body": { "1": "Sam" }
}
}
}Response
HTTP/1.1 202 Accepted
Content-Type: application/json
{
"id": "msg_cmqzr2pqj8009pyxcwhwz",
"status": "queued",
"to": "+919876543210",
"type": "template",
"conversation_id": "conv_cmqzr2pqj8009pyxcwh",
"meta_message_id": null,
"created_at": "2026-06-29T15:30:00.000Z"
}Request body
| Field | Type | Description |
|---|---|---|
to Required | string | Recipient phone in E.164 format (e.g. +919876543210). |
type Required | "text" | "template" | Message type. Determines which of the body fields below is read. |
text Conditional | string | Required when type="text". 1–4096 chars. |
template.name Conditional | string | Required when type="template". Must match an APPROVED template. |
template.language Conditional | string | Language code (e.g. en, en_US). Defaults to en. |
template.variables Optional | object | Grouped map. Keys: body, header, button_url. Each holds a positional 1-based map: { "1": "Sam" }. |
Get message status
GET /v1/messages/{id} — returns the current status of a previously-sent message.
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "msg_cmqzr2pqj8009pyxcwhwz",
"status": "delivered",
"to": "+919876543210",
"type": "template",
"conversation_id": "conv_cmqzr2pqj8009pyxcwh",
"meta_message_id": "wamid.HBgMOTE5OTQ5MDIyODIzFQ...",
"created_at": "2026-06-29T15:30:00.000Z",
"delivered_at": "2026-06-29T15:30:02.450Z",
"read_at": null
}Status values
| Value | Meaning |
|---|---|
| queued | Accepted by WapiSnap, not yet sent to Meta. |
| sent | Meta acknowledged the send. |
| delivered | Delivered to the recipient handset. |
| read | Read by the recipient. |
| failed | Send failed; see the `error` field on the message. |
Errors
Every error response carries the same envelope. request_id is the value to share with us when reporting an issue, it correlates to server-side logs.
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"error": {
"code": "unauthorized",
"message": "Invalid or revoked API key.",
"type": "authentication_error",
"request_id": "req_a1b2c3d4e5f6g7h8"
}
}HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": {
"code": "invalid_request",
"message": "Field 'to' must be in E.164 format (e.g. +919876543210).",
"type": "validation_error",
"param": "to",
"request_id": "req_a1b2c3d4e5f6g7h8"
}
}Common codes
| Code | HTTP | Meaning |
|---|---|---|
| unauthorized | 401 | Missing, invalid, or revoked API key. |
| forbidden | 403 | The key is valid but lacks permission for this operation. |
| not_found | 404 | The referenced resource does not exist or belongs to a different workspace. |
| invalid_request | 400 | Payload failed validation; check the `param` field for the offending field. |
| rate_limited | 429 | Workspace exceeded the per-second cap. Honor the Retry-After header. |
| template_not_found | 404 | No approved template with that name + language exists in this workspace. |
| template_not_approved | 400 | The template exists but is in PENDING or REJECTED state. |
| no_wa_number | 400 | The workspace has no WhatsApp number configured. |
| contact_marketing_throttled | 409 | Smart-retry has temporarily blocked marketing sends to this contact (Meta 131049 protection). |
| internal_error | 500 | Server-side failure. The `request_id` lets us trace it in logs. |
Rate limits
The default cap is 20 requests per second sustained, with bursts up to 30, per workspace. Requests beyond the burst budget receive 429 rate_limited with a Retry-After header in seconds. Need higher? Get in touch.
Versioning
The current version is v1, locked under the /v1 URL prefix. We will never make breaking changes to v1; additive fields (new optional request fields, new response fields) may land at any time. Future major versions will live alongside v1 under /v2 with at least 6 months of overlap.
Roadmap
- v1.1 — Idempotency keys (client-supplied), outbound webhooks for status + inbound replies.
- v1.2 — Media attachments (image, video, document, audio).
- v1.3 — Dedicated
POST /v1/flows/{id}/triggerfor standalone flow sends. - v2.x — Conversation list / inbox read endpoints.