Developer API

Send + receive WhatsApp programmatically: auth, scopes, every endpoint, and webhooks.

Base URL https://api.wamafy.com/api/v1/public · Create a key in Settings → API Access.

What the API does

Wamafy's public REST API lets your own systems (a CRM, an ecommerce backend, a helpdesk, Zapier/Make) drive WhatsApp programmatically - send messages, sync contacts, receive inbound messages via webhooks, run broadcasts, manage templates, and read analytics.

  • Base URL: https://api.wamafy.com/api/v1/public
  • Format: JSON in, JSON out. Every response is { "success": boolean, "data"?: ..., "error"?: { "message", "code" } }.
  • Two-way: send with the endpoints below; receive inbound customer messages by registering a webhook (see the last section).

Authentication

Create an API key at Settings → API Access → Create key. The full key (wamafy_live_…) is shown once - store it somewhere safe. Send it on every request:

Authorization: Bearer wamafy_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

A revoked key, or a key for a suspended workspace, returns 401. You can hold up to 25 active keys and revoke any of them at any time.

Scopes (least privilege)

Each key carries a set of scopes. When you create a key, either tick Full access (covers everything, current + future) or choose only the scopes that integration needs. Calling an endpoint your key isn't scoped for returns 403 with code API_SCOPE_MISSING.

ScopeGrants
templates:readList templates + their variables
templates:writeCreate + submit templates for review
messages:sendSend templates + session replies; window check
messages:readRead a contact's message history
contacts:readList + fetch contacts
contacts:writeCreate / update / opt-in / opt-out contacts
media:writeUpload media to reuse in sends
campaigns:sendTrigger a broadcast to a segment
analytics:readRead message + delivery stats
flows:triggerStart a chatbot flow for a contact

Keys created before scopes existed default to full access.

Quickstart - list templates + send one

Only approved templates can be sent to someone who hasn't messaged you yet (that's Meta's rule). List yours:

curl https://api.wamafy.com/api/v1/public/templates \
  -H "Authorization: Bearer wamafy_live_xxx"

Then send one - map your data onto each variable key:

curl -X POST https://api.wamafy.com/api/v1/public/messages \
  -H "Authorization: Bearer wamafy_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "to": "+919876543210", "templateName": "order_update", "variables": { "1": "Priya", "2": "#1234" } }'

The response returns { messageId, to, from, status }. Use ?number= on the list, or "from" on the send, to pick a specific WhatsApp number.

Sending messages

Templates (cold start) - POST /messages

Body: to, templateName, optional variables, from, language, and headerMediaUrl for media-header templates. This is the reliable way to reach someone at any time.

Session replies (inside the 24h window)

After a customer messages you, a 24-hour window opens where you can reply with free-form content. Check it first:

GET /window?to=919876543210   →   { "windowOpen": true, "expiresAt": "..." }
  • POST /messages/text - { to, text }
  • POST /messages/interactive - reply buttons (up to 3) or a list menu; the customer's tap returns on the inbound webhook
  • POST /messages/media - image/doc/video/audio (≤ 16 MB) from a mediaHandle or a public link

If there's no open conversation you get 400 NO_OPEN_CONVERSATION - send a template instead. Session sends count against your monthly message limit and report delivery status like template sends.

Contacts (CRM sync)

Phone is the natural key. Read needs contacts:read, writes need contacts:write.

  • GET /contacts - filter (search, status, consentStatus, tag) + paginate
  • GET /contacts/:phone - fetch one
  • POST /contacts - upsert by phone: updates if it exists, else creates; the response includes "created": true|false
  • PATCH /contacts/:phone - partial update (name / email / tags / status / consent)
  • POST /contacts/:phone/opt-out and /opt-in - consent shortcuts
  • GET /contacts/:phone/messages - message history, newest first, cursor-paginated with before / nextBefore (messages:read)
curl -X POST https://api.wamafy.com/api/v1/public/contacts \
  -H "Authorization: Bearer wamafy_live_xxx" -H "Content-Type: application/json" \
  -d '{ "phone": "+919876543210", "name": "Priya", "tags": ["vip"] }'

Media, campaigns, templates, analytics, flows

Media - POST /media (media:write)

Upload a file (multipart/form-data, field file) and get a mediaHandle to reuse in media sends. Or skip it and pass a public link straight to POST /messages/media.

Campaigns - POST /campaigns (campaigns:send)

Broadcast an approved template to a tag segment (or every active contact if tags is omitted). Creates + starts it immediately; opted-out contacts are skipped. Body: templateName, tags, variables, name.

Templates - POST /templates (templates:write)

Create + submit a template to Meta for review (same spec as the dashboard builder). Poll GET /templates?status=all to watch it move from PENDING to APPROVED, then it's sendable.

Analytics - GET /analytics (analytics:read)

Message totals (sent / delivered / read / failed / readRate) + a per-day breakdown over from/to (default last 30 days), in your workspace timezone.

Flows - POST /contacts/:phone/trigger-flow (flows:trigger)

Start a chatbot flow for a contact by flowId, with optional variables that land in the run context. Returns started or skipped.

Webhooks - receiving events

To receive (not just send), register a webhook at Settings → API Access → Webhooks. Pick the events you want; you'll get a signing secret (shown once). Wamafy POSTs the event JSON to your URL.

message.inbound

Fires whenever a customer messages one of your numbers - text, button/list tap (interactiveReplyId), media (mediaId), and the Click-to-WhatsApp ad referral on the first message. Pair it with the session-send endpoints for a full two-way integration.

POST <your url>
X-Wamafy-Event: message.inbound
X-Wamafy-Signature: sha256=<hmac>

{ "event": "message.inbound", "occurredAt": "...",
  "data": { "messageId": "wamid...", "from": "919876543210", "type": "text",
            "text": "Is this in stock?", "conversationId": "...", "leadId": "..." } }

Verify the signature

X-Wamafy-Signature is sha256= + HMAC-SHA256 of the raw request body, keyed with your webhook's signing secret. Recompute it and compare - reject the request if it doesn't match. There's also a separate status webhook that pushes delivery/read/failed receipts for messages you sent.

Respond 2xx quickly. Endpoints that keep failing auto-disable; re-enable from the dashboard. Use Send test to check reachability.

Errors, limits + the request log

Errors come back as { "success": false, "error": { "message", "code" } } with an HTTP status. Common codes:

StatusMeaning
401Missing / invalid / revoked key
403Key missing the required scope, or monthly API cap reached
404Template / contact / flow / number not found
400Bad request - e.g. NO_OPEN_CONVERSATION, template not approved
422Validation failed (bad body shape)
429Rate limit - 60 requests/min per key
  • Rate limit: 60 requests/minute per key, plus your plan's monthly message + campaign caps.
  • Request log: every call is recorded at Settings → API Access → Request log - endpoint, status, outcome, and the error reason on failures. Your first stop for debugging.

Ready to build? Create an API key from your workspace.

Go to Settings → API Access
Live demo