WhatsApp Gateway API Documentation | WaMark

WhatsApp Gateway API


API Reference v1 Get your API key →

WaMark WhatsApp API

One REST API for everything WhatsApp — send messages and templates, verify OTPs, manage contacts and labels, read conversations, trigger chatbots and drip sequences, and read commerce catalogs. Built on the official WhatsApp Business Platform.

Base URL  https://api.wamark.in/v1  ·  Every request is JSON over HTTPS and authenticated with your API key.

Introduction

The WaMark API lets your app or CRM drive your WhatsApp Business number programmatically. Create an API key in Settings → Developer, choose the scopes you need, and start calling. Every send is metered against your plan and billed at the Meta rate — no hidden markup.

Authentication

Send your key on every request as a Bearer token (or the x-api-key header):

# Bearer header (recommended)
Authorization: Bearer wam_xxxxxxxxxxxxxxxxxxxxxxxx

# …or
x-api-key: wam_xxxxxxxxxxxxxxxxxxxxxxxx

Keys look like wam_… and are shown once at creation. Each key is scoped to a single workspace; all data access is tenant-isolated at the database level.

Scopes & keys

A key only reaches the endpoints its scopes allow — a call to an out-of-scope endpoint returns 403. Grant the least you need.

ScopeGrants
messagesSend messages, bulk send, upload media, read message status
otpSend & verify WhatsApp OTP codes
contactsRead, create, update, delete contacts & labels
templatesList your approved message templates
conversationsRead chat history, assign chats to agents
chatbotsList & trigger chatbot flows
sequencesManage drip sequences & enrol contacts
catalogRead commerce catalogs & products
accountRead account info, plan & quota

Rate limits

Each key is limited per IP: 120 requests/min by default, with tighter caps on sensitive endpoints (messages 60/min, bulk 10/min, OTP 10/min). Exceeding a limit returns 429. Every message send also checks your plan quota and debits your per-number wallet at the Meta rate.

Errors

Errors use standard HTTP status codes with a JSON body { "message": "…", "statusCode": 4xx }.

CodeMeaning
400Bad request — a required field is missing or malformed
401Missing or invalid API key
403Your key is missing the required scope
404The resource (message, contact, conversation…) was not found
429Rate limit exceeded — slow down and retry
WhatsApp #131047 / #132012 / #132000 — Meta-side errors surface in the send response’s error field. Free text only delivers inside the 24-hour customer-service window; outside it, send an approved template.

Send a message

POST/v1/messagesmessages

Send a single text or media message. Media can be a public mediaUrl or a mediaId from /v1/media.

Request — text

curl -X POST https://api.wamark.in/v1/messages \
-H "Authorization: Bearer wam_xxx" -H "Content-Type: application/json" \
-d '{"to":"15551234567","body":"Hello from our app 👋"}'

Request — document

-d '{"to":"15551234567","type":"document","mediaUrl":"https://example.com/invoice.pdf","filename":"invoice.pdf","caption":"Your invoice 🧾"}'

Response

{ "id": "2567b319-…", "wamid": "wamid.HBg…", "status": "sent", "error": null }
FieldNotes
to requiredRecipient in international format (national numbers are auto-prefixed with the workspace country code)
bodyText (required for a text message)
typedocument / image / video / audio for media
mediaUrl / mediaIdPublic URL Meta can fetch, or an uploaded media id
filename, captionOptional, for media messages

Bulk send

POST/v1/messages/bulkmessages

Fan one send out to up to 200 recipients per call, with per-recipient variables. Two modes.

Template mode — for cold audiences

Sends one approved template per recipient; the template’s buttons ride along. variables fill the body {{vars}}; buttonUrl injects a per-recipient value into a dynamic URL button.

-d '{
"template": "order_update", "language": "en",
"recipients": [
{"to":"15551230001","variables":{"name":"Ravi","1":"BK-1029"},"buttonUrl":"1029"},
{"to":"15551230002","variables":{"name":"Sana","1":"BK-1030"},"buttonUrl":"1030"}
], "fallback": ""
}'

For finer control (multiple buttons, or non-URL subtypes), send a buttons array per recipient instead of buttonUrl. index is the button’s 0-based position in the approved template, subtype is url, quick_reply or copy_code, and value is the per-recipient value.

-d '{
"template": "due_date_notification", "language": "en",
"recipients": [
{ "to":"9XXXXXXXXX",
"variables": {"1":"1234","2":"value1","3":"value1"},
"buttons": [ {"index":0,"subtype":"url","value":"1029"} ] }
]
}'

Interactive mode — tappable buttons, no pre-approval

Reply buttons defined at send time. Delivers only inside the 24-hour window; cold recipients return status:"skipped".

-d '{
"type": "buttons", "body": "Your order is ready. Collect today?",
"buttons": [ {"label":"Yes"}, {"label":"Reschedule"} ],
"recipients": [ {"to":"15551230001"}, {"to":"15551230002"} ]
}'

Response

{ "total":2, "sent":2, "skipped":0, "failed":0,
"results":[ {"to":"…","status":"sent","id":"…","wamid":"…"} ] }

Upload media

POST/v1/mediamessages

Host a file and get a public mediaUrl to pass to /v1/messages. Allowed: PDF, JPEG/PNG/WebP/GIF, MP4 (≤16 MB). File contents are verified against the declared type.

curl -X POST https://api.wamark.in/v1/media \
-H "Authorization: Bearer wam_xxx" \
-F "file=@invoice.pdf"
→ { "mediaUrl": "https://api.wamark.in/uploads/…/x.pdf", "type": "document" }

Message status

GET/v1/messages/{id}/statusmessages

Look up delivery status by the id we return on send, or the WhatsApp wamid. Reflects the latest Meta callback.

curl https://api.wamark.in/v1/messages/MESSAGE_ID/status -H "Authorization: Bearer wam_xxx"
→ { "status": "delivered", "type":"text", "to":"…", "sentAt":"…" }

Status lifecycle: accepted → sent → delivered → read (or failed).

Send & verify OTP

POST/v1/otp/sendotp
POST/v1/otp/verifyotp

WaMark generates, stores and expires the code for you.

curl -X POST https://api.wamark.in/v1/otp/send  -d '{"to":"15551234567","brand":"WaMark","codeLength":6}'
curl -X POST https://api.wamark.in/v1/otp/verify -d '{"to":"15551234567","code":"123456"}'
→ { "verified": true }

Contacts

GET/v1/contactscontacts
GET/v1/contacts/{id}contacts
POST/v1/contactscontacts
PATCH/v1/contacts/{id}contacts
DELETE/v1/contacts/{id}contacts

Full contact lifecycle. GET /v1/contacts accepts search, stage, company, country, source, vip filters.

curl -X POST https://api.wamark.in/v1/contacts \
-d '{"fullName":"Jane Doe","phone":"15551234567","email":"jane@example.com"}'

curl -X PATCH https://api.wamark.in/v1/contacts/CONTACT_ID \
-d '{"attributes":{"plan":"pro"},"lifecycleStage":"customer"}'

Labels

GET/v1/labelscontacts
POST/v1/labelscontacts
POST/v1/contacts/{id}/labelscontacts
DELETE/v1/contacts/{id}/labelscontacts

Create and list labels, then assign or remove them on a contact. Assigning merges with the contact’s existing labels.

curl -X POST https://api.wamark.in/v1/labels -d '{"name":"VIP","color":"#22c55e"}'
curl -X POST https://api.wamark.in/v1/contacts/CONTACT_ID/labels -d '{"labelIds":["LABEL_ID"]}'

Conversations

GET/v1/conversationsconversations
GET/v1/conversations/{id}/messagesconversations
POST/v1/conversations/{id}/assignconversations

List conversations, read a thread’s history (paged — never marks the agent’s unread badge), and assign a chat to a team member (userId: null to unassign).

curl "https://api.wamark.in/v1/conversations/CONVERSATION_ID/messages?limit=50" -H "Authorization: Bearer wam_xxx"
curl -X POST https://api.wamark.in/v1/conversations/CONVERSATION_ID/assign -d '{"userId":"USER_ID"}'

Templates

GET/v1/templatestemplates

List your message templates (optionally ?status=approved) so you know exactly what you can send.

curl "https://api.wamark.in/v1/templates?status=approved" -H "Authorization: Bearer wam_xxx"

Chatbots

GET/v1/chatbotschatbots
POST/v1/chatbots/{id}/triggerchatbots

List your chatbots and run one on demand for a phone number — the flow executes from its start. A cold contact needs the bot’s first step to be a template.

curl -X POST https://api.wamark.in/v1/chatbots/CHATBOT_ID/trigger -d '{"to":"15551234567"}'

Drip sequences

GET/v1/sequencessequences
POST/v1/sequencessequences
POST/v1/sequences/{id}/enrollsequences
POST/v1/sequences/{id}/unenrollsequences

A scheduled, multi-step message journey. Each step waits afterMinutes, then sends a template (works any time) or free text (in-window). Opted-out contacts are dropped automatically.

curl -X POST https://api.wamark.in/v1/sequences -d '{
"name":"Welcome", "status":"active",
"steps":[
{"afterMinutes":0, "type":"template","template":"welcome","language":"en"},
{"afterMinutes":1440,"type":"text","body":"Any questions? 😊"}
] }'


curl -X POST https://api.wamark.in/v1/sequences/SEQ_ID/enroll -d '{"to":"15551234567"}'

Catalog

GET/v1/catalogscatalog
GET/v1/catalogs/{id}/productscatalog

Read your connected commerce catalogs and their products (paged via ?after=).

curl https://api.wamark.in/v1/catalogs -H "Authorization: Bearer wam_xxx"

Account

GET/v1/meaccount

Your workspace’s plan plus messaging and AI quota (used / included / remaining).

curl https://api.wamark.in/v1/me -H "Authorization: Bearer wam_xxx"

OpenAPI spec

GET/v1/openapi.json

The full machine-readable reference (OpenAPI 3.0, no key required) — import it into Postman, Insomnia or any codegen tool.

https://api.wamark.in/v1/openapi.json

Also available — the simple gateway

POSTGET/gateway/send

A one-call, no-header endpoint for legacy CRM “SMS gateway” screens — pass your token and message as request parameters. Ideal when a system can only send a single URL with query params.

curl "https://api.wamark.in/gateway/send?token=YOUR_TOKEN&to=15551234567&text=Hello"
For everything else — bulk, media, contacts, automation — use the /v1 API above with a wam_ key.

Questions? Talk to us · Manage keys in Settings → Developer.

Tag

Share

Any Questions? Our support team is available 24/7

Live Chat Now