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/messages | Send a message |
| GET | /api/v1/messages | List messages (newest first) |
| GET | /api/v1/messages/:id | Get one message and its delivery status |
| GET | /api/v1/contacts | List contacts |
| POST | /api/v1/contacts | Create or update a contact |
| GET | /api/v1/contacts/:wa_id | Get a contact |
| PATCH | /api/v1/contacts/:wa_id | Update a contact |
| DELETE | /api/v1/contacts/:wa_id | Delete a contact |
| GET | /api/v1/templates | List message templates |
| POST | /api/v1/templates | Submit a template for approval |
| DELETE | /api/v1/templates?name=&id= | Delete a template |
| GET | /api/v1/campaigns | List campaigns with delivery counts |
| POST | /api/v1/campaigns | Start or schedule a campaign |
| PATCH | /api/v1/campaigns | Pause, resume or cancel a campaign |
| GET | /api/v1/automations | Automation settings and rules |
| POST | /api/v1/automations | Create a rule, save settings or add starter rules |
| PATCH | /api/v1/automations | Update a rule or switch it on/off |
| DELETE | /api/v1/automations?id= | Delete a rule |
| GET | /api/v1/flows | List bot flows |
| POST | /api/v1/flows | Create a bot flow (starts off) |
| PATCH | /api/v1/flows | Switch a bot flow on/off |
| DELETE | /api/v1/flows?id= | Delete a bot flow |
| GET | /api/v1/forms | List WhatsApp forms |
| POST | /api/v1/forms | Create a draft form or publish one |
| GET | /api/v1/forms/:id/responses | Answers customers sent |
| GET | /api/v1/phone-numbers | List your WhatsApp numbers |
| GET | /api/v1/media/:message_id | Download 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." } }| 401 | unauthorized | Missing, invalid or revoked API key |
| 400 | invalid_recipient / invalid_text / invalid_template / invalid_media | Request body problems |
| 400 | whatsapp_error | WhatsApp rejected the message (details.whatsapp_code) |
| 404 | not_found / phone_number_not_found | Unknown message, contact or number |
| 400 | invalid_campaign / invalid_automation / invalid_flow / invalid_form | Validation problems |
| 402 | plan_expired / upgrade_required | Renew or upgrade on the Billing page |
| 409 | number_disconnected | Reconnect the number in the dashboard |
| 409 | invalid_state | The campaign cannot be paused, resumed or cancelled now |
| 422 | reply_window_closed | Use a template outside the 24-hour window |
| 429 | rate_limited | Slow down; see Retry-After |