← Wapify

Wapify REST API

Send WhatsApp messages from your own software and receive replies and delivery updates on your server. Create API keys and webhook endpoints under API & Webhooks in the dashboard.

Authentication

Send your API key in the Authorization header. Keys start with wpf_live_. Keep them on your server; anyone with a key can message your customers.

curl https://wapify.io/api/v1/phone-numbers \
  -H "Authorization: Bearer wpf_live_YOUR_KEY"

Each key allows 600 requests per minute. Above that you get 429 rate_limited with a Retry-After header.

Endpoints

POST/api/v1/messagesSend a message
GET/api/v1/messagesList messages (newest first)
GET/api/v1/messages/:idGet one message and its delivery status
GET/api/v1/contactsList contacts
POST/api/v1/contactsCreate or update a contact
GET/api/v1/contacts/:wa_idGet a contact
PATCH/api/v1/contacts/:wa_idUpdate a contact
DELETE/api/v1/contacts/:wa_idDelete a contact
GET/api/v1/templatesList message templates
POST/api/v1/templatesSubmit a template for approval
DELETE/api/v1/templates?name=&id=Delete a template
GET/api/v1/campaignsList campaigns with delivery counts
POST/api/v1/campaignsStart or schedule a campaign
PATCH/api/v1/campaignsPause, resume or cancel a campaign
GET/api/v1/automationsAutomation settings and rules
POST/api/v1/automationsCreate a rule, save settings or add starter rules
PATCH/api/v1/automationsUpdate a rule or switch it on/off
DELETE/api/v1/automations?id=Delete a rule
GET/api/v1/flowsList bot flows
POST/api/v1/flowsCreate a bot flow (starts off)
PATCH/api/v1/flowsSwitch a bot flow on/off
DELETE/api/v1/flows?id=Delete a bot flow
GET/api/v1/formsList WhatsApp forms
POST/api/v1/formsCreate a draft form or publish one
GET/api/v1/forms/:id/responsesAnswers customers sent
GET/api/v1/phone-numbersList your WhatsApp numbers
GET/api/v1/media/:message_idDownload the file of a media message

Send a message

to is the customer's number with country code. from is one of your phone_number_id values (optional if you have one number). Free-form messages (text, media, interactive) can only be sent within 24 hours of the customer's last message; otherwise send an approved template.

# Template (works any time)
curl -X POST https://wapify.io/api/v1/messages \
  -H "Authorization: Bearer wpf_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "919876543210",
    "type": "template",
    "template": {
      "name": "order_update",
      "language": "en",
      "components": [
        { "type": "body", "parameters": [ { "type": "text", "text": "Asha" }, { "type": "text", "text": "#1042" } ] }
      ]
    }
  }'

# One-time login code (an Authentication template from the Templates page)
curl -X POST https://wapify.io/api/v1/messages \
  -H "Authorization: Bearer wpf_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "to": "919876543210", "type": "template",
        "template": { "name": "login_code", "language": "en", "otp_code": "482913" } }'

# Text (within 24 hours of the customer's last message)
curl -X POST https://wapify.io/api/v1/messages \
  -H "Authorization: Bearer wpf_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "to": "919876543210", "type": "text", "text": { "body": "Your order has shipped!" } }'

# Image / document / video / audio by public https link
-d '{ "to": "919876543210", "type": "document",
      "document": { "link": "https://example.com/invoice.pdf", "filename": "invoice.pdf", "caption": "Your invoice" } }'

Response (201):

{
  "data": {
    "id": "6f1c…",                 // use with GET /api/v1/messages/:id
    "wa_message_id": "wamid.HBg…",
    "status": "sent",
    "from": "1155532314318999",
    "to": "919876543210",
    "type": "template",
    "created_at": "2026-10-26T11:20:00.000Z"
  }
}

status then moves to delivered, read or failed — fetch the message, or subscribe to message.status webhooks.

Templates, campaigns, automations, bot flows and forms

# Submit a template ({{1}} needs an example)
curl -X POST https://wapify.io/api/v1/templates -H "Authorization: Bearer wpf_live_YOUR_KEY" -H "Content-Type: application/json" \
  -d '{ "template": { "name": "order_shipped", "language": "en", "category": "UTILITY",
        "body": "Hi {{1}}, your order has shipped", "bodyExamples": ["Asha"],
        "buttons": [{ "type": "QUICK_REPLY", "text": "Track" }] } }'

# Campaign to contacts tagged vip; {{1}} = each contact's name
curl -X POST https://wapify.io/api/v1/campaigns -H "Authorization: Bearer wpf_live_YOUR_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Diwali", "template_name": "diwali_offer", "template_language": "en",
        "variables": { "body": [{ "source": "name", "fallback": "there" }] },
        "audience": { "type": "tags", "tags": ["vip"] }, "scheduled_at": null }'

# Keyword auto-reply
curl -X POST https://wapify.io/api/v1/automations -H "Authorization: Bearer wpf_live_YOUR_KEY" -H "Content-Type: application/json" \
  -d '{ "rule": { "name": "Price", "trigger_type": "keyword", "match_type": "contains", "keywords": ["price"],
        "reply": { "type": "text", "text": "Our price list: https://example.com/prices" } } }'

# Switch a bot flow on (flows are built in the dashboard editor)
curl -X PATCH https://wapify.io/api/v1/flows -H "Authorization: Bearer wpf_live_YOUR_KEY" -H "Content-Type: application/json" \
  -d '{ "id": "FLOW_ID", "enabled": true }'

# Send a form in a chat (within 24 hours of the customer's last message)
curl -X POST https://wapify.io/api/v1/messages -H "Authorization: Bearer wpf_live_YOUR_KEY" -H "Content-Type: application/json" \
  -d '{ "to": "919876543210", "type": "form", "form": { "id": "FORM_ID", "body": "Book your visit", "cta": "Open form" } }'

Variable sources for campaigns are static (with value), name, wa_id and email (with a fallback). Forms take { name, category, definition: { title, submitLabel, fields: [{ label, type, required, options }] } }.

Contacts

curl -X POST https://wapify.io/api/v1/contacts \
  -H "Authorization: Bearer wpf_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "wa_id": "919876543210", "name": "Asha", "email": "asha@example.com", "tags": ["vip", "diwali-2026"] }'

Tags are used for campaign audiences. opted_out: true means the customer replied STOP; campaigns skip them.

Webhooks to your server

We POST JSON to your endpoint for each event. Respond with any 2xx within 8 seconds; other responses are retried up to 3 times.

POST /your/webhook
X-Wapify-Event: message.received
X-Wapify-Delivery: 2b7c…
X-Wapify-Signature: t=1792999999,v1=5f2a…

{
  "id": "2b7c…",
  "event": "message.received",
  "created_at": "2026-10-26T11:21:00.000Z",
  "data": {
    "id": "8d0e…",
    "from": "919876543210",
    "contact_name": "Asha",
    "phone_number_id": "1155532314318999",
    "type": "text",
    "text": "Is this available in blue?",
    "media_url": null,              // for media: GET it with your API key
    "message": { /* original WhatsApp message object */ },
    "timestamp": "2026-10-26T11:20:58.000Z"
  }
}

message.status events carry id, wa_message_id, status, recipient and errors.

Verify the signature with your endpoint's signing secret (whsec_…) before trusting a request:

// Node.js / Express — use the raw request body, not re-serialized JSON
const crypto = require('crypto');

function isFromWapify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false; // older than 5 minutes
  const expected = crypto.createHmac('sha256', secret).update(parts.t + '.' + rawBody).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}

app.post('/wapify/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  if (!isFromWapify(req.body.toString(), req.get('X-Wapify-Signature'), process.env.WAPIFY_WEBHOOK_SECRET)) {
    return res.sendStatus(401);
  }
  const event = JSON.parse(req.body);
  // ... handle event.event / event.data
  res.sendStatus(200);
});

Errors

Errors use HTTP status codes and a stable code:

{ "error": { "code": "reply_window_closed", "message": "The customer has not written in the last 24 hours. Send an approved template instead." } }
401unauthorizedMissing, invalid or revoked API key
400invalid_recipient / invalid_text / invalid_template / invalid_mediaRequest body problems
400whatsapp_errorWhatsApp rejected the message (details.whatsapp_code)
404not_found / phone_number_not_foundUnknown message, contact or number
400invalid_campaign / invalid_automation / invalid_flow / invalid_formValidation problems
402plan_expired / upgrade_requiredRenew or upgrade on the Billing page
409number_disconnectedReconnect the number in the dashboard
409invalid_stateThe campaign cannot be paused, resumed or cancelled now
422reply_window_closedUse a template outside the 24-hour window
429rate_limitedSlow down; see Retry-After