WATI Pro Developer Documentation
Integrate WhatsApp into any website, CRM or backend in minutes. Send text + media, run campaigns, manage contacts, build auto-reply bots, and receive inbound messages via signed webhooks.
Introduction
The WATI Pro API is a JSON HTTPS REST API. All requests use:
Base URL: https://wati.pro
Content: application/json
Auth: Authorization: Bearer wati_xxxxxxxx...Every endpoint listed here is reachable from any backend (Node, PHP, Python, Ruby, .NET, Go, Java, n8n, Zapier, Make, etc.) or directly from a server-side handler on your website. Do not call these endpoints from a browser — your API key must never be exposed to end users.
OPTIONS preflight.Authentication
Create an API key in Settings → API Keys. The key is shown only once and looks like wati_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
Send it as a Bearer token on every request:
curl https://wati.pro/api/public/v1/send \
-H "Authorization: Bearer wati_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"to":"+923001234567","message":"Hi from API"}'Failed auth returns 401 Unauthorized. Revoke or disable a key any time from the dashboard.
Plan limits & quotas
Every API call is checked against your active plan. When you hit a cap the request returns 409 Conflict with a clear error message and the dashboard shows an upgrade prompt. Counters reset on your billing cycle (messages), daily at 00:00 UTC (AI replies), or never (devices, seats, rules).
| Resource | Free | Starter | Pro | Agency |
|---|---|---|---|---|
| Connected devices | 1 | 1 | 3 | 10 |
| Outbound messages / day | 50 | 500 | 2,000 | 10,000 |
| Keyword auto-reply rules | 3 | 20 | Unlimited | Unlimited |
| Team Inbox seats | 1 | 2 | 5 | 25 |
| AI Smart Replies / day | 0 | 100 | 1,000 | 10,000 |
| REST API access | — | — | ✔ | ✔ |
| Signed inbound webhooks | — | — | ✔ | ✔ |
Soft-rate limit per API key defaults to 60 req/min. Admins can tune per-key limits atAdmin → Rate Limits.
Send a text message
/api/public/v1/sendSends a WhatsApp text message from one of your connected devices.
Request body
{
"to": "+923001234567", // required — E.164 or local digits
"message": "Hello from WATI", // required — up to 4096 chars
"deviceId": "uuid-optional" // optional — uses first connected device if omitted
}curl
curl -X POST https://wati.pro/api/public/v1/send \
-H "Authorization: Bearer wati_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+923001234567",
"message": "Your OTP is 123456"
}'200 response
{
"ok": true,
"id": "9b7c...uuid",
"messageId": "3EB0F5C8...",
"status": "sent",
"to": "923001234567"
}Send media — image, document, video, audio
/api/public/v1/send-mediaPass a public HTTPS URL pointing at your file. The gateway downloads it once and forwards it as a real WhatsApp media message.
Request body
{
"to": "+923001234567", // required
"type": "image" | "video" | "audio" | "document", // required
"url": "https://your-cdn.com/invoice.pdf", // required, public URL
"caption": "Optional caption", // optional (image/video/document)
"fileName": "invoice.pdf", // recommended for document
"mimetype": "application/pdf", // optional, auto-detected
"deviceId": "uuid-optional"
}Send an image
curl -X POST https://wati.pro/api/public/v1/send-media \
-H "Authorization: Bearer wati_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+923001234567",
"type": "image",
"url": "https://picsum.photos/800/600.jpg",
"caption": "Check out our new product"
}'Send a PDF document
curl -X POST https://wati.pro/api/public/v1/send-media \
-H "Authorization: Bearer wati_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+923001234567",
"type": "document",
"url": "https://example.com/files/invoice-2026-001.pdf",
"fileName": "invoice-2026-001.pdf",
"mimetype": "application/pdf",
"caption": "Your invoice for January"
}'Send a video
curl -X POST https://wati.pro/api/public/v1/send-media \
-H "Authorization: Bearer wati_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+923001234567",
"type": "video",
"url": "https://example.com/demo.mp4",
"caption": "60-second product demo"
}'Send a voice note / audio
curl -X POST https://wati.pro/api/public/v1/send-media \
-H "Authorization: Bearer wati_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+923001234567",
"type": "audio",
"url": "https://example.com/welcome.mp3",
"mimetype": "audio/mpeg"
}'url must be publicly reachable over HTTPS so the gateway can fetch the file. Common WhatsApp size limits apply (image 5 MB, video 16 MB, audio 16 MB, document 100 MB).Look up a sent message
/api/public/v1/messages/{id}id can be either the internal UUID returned by /sendor the WhatsApp messageId.
curl https://wati.pro/api/public/v1/messages/9b7c...uuid \
-H "Authorization: Bearer wati_YOUR_KEY"{
"message": {
"id": "9b7c...",
"message_id": "3EB0F5C8...",
"direction": "out",
"peer": "923001234567",
"text": "Hello from WATI",
"status": "sent",
"created_at": "2026-06-10T12:34:56Z"
}
}Bulk campaigns
/api/public/v1/campaignsCreate a campaign that delivers in batches. Returns immediately with a campaign id; use the run endpoint or our cron to process batches. Variables {{name}} and {{phone}} are replaced per-recipient.
Request body
{
"name": "January promo",
"message": "Hi {{name}}, today only — 20% off!",
"deviceId": "uuid-optional",
"delaySeconds": 3, // 1-60 between messages
"scheduledAt": "2026-07-01T10:00:00Z", // optional ISO-8601, future date for scheduled campaign
"tag": "vip", // optional — send only to contacts with this tag
"recipients": [ // recipients[] OR contactListId OR tag
{ "name": "Ali", "phone": "+923001234567" },
{ "name": "Sara", "phone": "+923009876543" }
],
"contactListId": "uuid-of-saved-list"
}Provide exactly one of recipients, contactListId, or tag. When tag is used the campaign auto-resolves every contact in your CRM carrying that tag at run time.
curl
curl -X POST https://wati.pro/api/public/v1/campaigns \
-H "Authorization: Bearer wati_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Welcome blast",
"message": "Hi {{name}}, thanks for joining!",
"delaySeconds": 5,
"recipients": [
{"name":"Ali","phone":"+923001111111"},
{"name":"Sara","phone":"+923002222222"}
]
}'Trigger the next batch
/api/public/v1/campaigns/{id}/runcurl -X POST https://wati.pro/api/public/v1/campaigns/<campaignId>/run \
-H "Authorization: Bearer wati_YOUR_KEY"Contacts
Contacts use Supabase-issued user JWT (same key style — wati_ keys also work here).
/api/contactscurl https://wati.pro/api/contacts \
-H "Authorization: Bearer wati_YOUR_KEY"/api/contactscurl -X POST https://wati.pro/api/contacts \
-H "Authorization: Bearer wati_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"contacts": [
{ "name": "Ali", "phone": "+923001234567", "tags": ["lead","jan26"] },
{ "name": "Sara", "phone": "+923009876543", "tags": ["customer"] }
]
}'/api/contactscurl -X DELETE https://wati.pro/api/contacts \
-H "Authorization: Bearer wati_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "ids": ["uuid-1","uuid-2"] }'Auto-reply rules
/api/auto-repliescurl https://wati.pro/api/auto-replies -H "Authorization: Bearer wati_YOUR_KEY"/api/auto-repliescurl -X POST https://wati.pro/api/auto-replies \
-H "Authorization: Bearer wati_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"keyword": "price",
"match_type": "contains", // contains | exact | starts_with
"response": "Our pricing: https://wati.pro/pricing",
"responses": [
"Our pricing: https://wati.pro/pricing",
"Need a custom quote? Reply YES."
],
"step_delay_seconds": 2,
"active": true
}'/api/auto-repliescurl -X DELETE https://wati.pro/api/auto-replies \
-H "Authorization: Bearer wati_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "id": "rule-uuid" }'AI Smart Reply (multi-language)
When an inbound message does NOT match any keyword rule and AI Smart Reply is enabled, WATI Pro calls the Lovable AI Gateway (google/gemini-3-flash-preview) with the last 5 messages from that conversation, auto-detects the customer's language, and replies in the same language. Replies are logged in Auto-Reply → Logand count against your daily AI quota.
Configure via dashboard
Go to Auto-Reply → AI Smart Reply to toggle it on, set a custom system prompt (your business voice), and pick a preferred fallback language.
Decision flow
incoming message
├─ matches a keyword rule? → send keyword response, STOP
├─ AI Smart Reply enabled? → call Lovable AI Gateway
│ ├─ over daily AI quota? → silent skip, log "quota_exceeded"
│ └─ ok → reply in customer's language, log "ai"
└─ neither → no reply (manual handling in Team Inbox)Supported languages (auto-detected)
English, Urdu, Hindi, Arabic, Spanish, French, Portuguese, Bengali, Indonesian, Turkish, Russian, German, Italian, Dutch and 80+ more — anything Gemini understands.
Shared Team Inbox
The Team Inbox is currently a dashboard feature — multiple agents share one WhatsApp number, conversations can be assigned to a teammate, and statuses are open, pending or resolved. Outbound replies sent from the inbox go through the same gateway as the REST API, so they appear in /api/public/v1/messages/{id} like any other message.
Conversation object (server-function response)
{
"id": "uuid",
"device_id": "uuid",
"peer": "923001234567",
"assigned_to": "user-uuid | null",
"status": "open | pending | resolved",
"unread_count": 3,
"last_message_at": "2026-06-11T09:12:33Z"
}Roles & permissions
- Agent — sees & replies to assigned conversations; cannot reassign or change billing.
- Manager — sees ALL conversations on the account, can assign/resolve, view analytics.
- Admin — full project access incl. devices, API keys, team roles.
- Super Admin — platform-wide (multi-tenant).
/send (replies).Analytics
The Analytics dashboard aggregates the last 30 days: outbound vs inbound message volume per day, campaign success/failure rates, AI vs keyword reply share, and top-performing templates.
Programmatic access
For now derive your own counters from /api/public/v1/messages and campaign listings. A dedicated /api/public/v1/analytics/summaryendpoint is on the v2 roadmap.
Incoming-message webhooks
Configure a webhook URL in Settings → Gateway. Every inbound WhatsApp message is POSTed to your URL with an HMAC signature you should verify.
Headers we send
POST https://your-app.com/wati-hook
Content-Type: application/json
X-Wati-Signature: sha256=<hex hmac> # HMAC-SHA256 of the raw body
X-Wati-Timestamp: 1733832000Body
{
"event": "message.received",
"deviceId": "uuid",
"from": "923001234567",
"to": "923009999999",
"text": "hello",
"messageId": "3EB0...",
"type": "text" | "image" | "video" | "audio" | "document",
"mediaUrl": "https://...", // when type != text
"timestamp": 1733832000
}Verify the signature (Node.js)
import crypto from "crypto";
function verify(rawBody, signatureHeader, secret) {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expected)
);
}Event types
message.received— inbound WhatsApp message (text or media)message.status— sent / delivered / read / failedconversation.assigned— Team Inbox assignment changedcampaign.completed— bulk campaign finished, includes sent/failed counts
Delivery & retries
Your endpoint must respond 2xx within 10s. Non-2xx or timeout triggers exponential-backoff retries (1m, 5m, 30m, 2h, 12h — then dropped). The same messageId may arrive more than once — make your handler idempotent.
Meta WhatsApp Cloud API setup
Use Meta's official WhatsApp Cloud API instead of the local Baileys gateway. Required for verified businesses, green-tick, and high-volume sending. WATI Pro handles webhook routing per tenant, signature verification, and provider switching — you only paste credentials.
Step 1 — Create a Meta app
- Go to developers.facebook.com/apps → Create App → choose Business.
- Add the WhatsApp product to your app.
- In WhatsApp → API Setup, copy the Phone Number ID and WhatsApp Business Account ID (WABA).
Step 2 — Generate a permanent access token
- Open Business Settings → Users → System Users → Add (role: Admin).
- Assign the system user to your WhatsApp Account with Full control.
- Click Generate Token → select your app → grant
whatsapp_business_messagingandwhatsapp_business_management→ never expires.
Step 3 — Paste credentials in WATI Pro
Open Settings → Gateway, choose the Meta Cloud API card, and fill in:
- Phone Number ID — from Meta API Setup
- Business Account ID (WABA)
- Permanent Access Token — from step 2
- App Secret — Meta App → Settings → Basic
- Webhook Verify Token — any random string you make up (e.g.
my-tenant-7f3a)
Step 4 — Configure the webhook in Meta
In Meta App → WhatsApp → Configuration → Webhook, paste your per-tenant URL (shown in Settings → Gateway after saving credentials):
Callback URL: https://wati.pro/api/public/cloud-api-webhook/<your-tenant-id>
Verify Token: <the same string you saved in step 3>Click Verify and Save. Then subscribe to the messages field. All inbound messages and delivery statuses will flow into your Team Inbox automatically — signed with HMAC-SHA256 using your App Secret.
Step 5 — Send your first message
Cloud API requires a pre-approved template for first contact (outside the 24-hour customer service window). Create a template in Meta Business Manager → WhatsApp Manager → Message Templates, then send from any of the WATI Pro endpoints below — the platform auto-routes to Meta when your provider is set to Cloud API.
curl -X POST https://wati.pro/api/public/v1/send \
-H "Authorization: Bearer wati_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "to": "+923001234567", "message": "Hello from Cloud API" }'Invalid signaturein webhook logs → App Secret mismatch.(#131030) Recipient phone number not in allowed list→ add test numbers in Meta API Setup, or graduate your number to production.(#132000) Template does not exist→ template not approved yet, or wrong language code.
Errors & rate limits
Per-key rate limits are configurable by admins in Admin → Rate Limits. Errors always return JSON: { "error": "message" }.
Code samples
Node.js (fetch)
await fetch("https://wati.pro/api/public/v1/send", {
method: "POST",
headers: {
"Authorization": "Bearer " + process.env.WATI_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
to: "+923001234567",
message: "Hi from Node",
}),
});PHP (cURL)
<?php
$ch = curl_init("https://wati.pro/api/public/v1/send");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("WATI_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"to" => "+923001234567",
"message" => "Hi from PHP",
]),
]);
echo curl_exec($ch);Python (requests)
import os, requests
requests.post(
"https://wati.pro/api/public/v1/send-media",
headers={"Authorization": f"Bearer {os.environ['WATI_KEY']}"},
json={
"to": "+923001234567",
"type": "document",
"url": "https://example.com/file.pdf",
"fileName": "file.pdf",
"caption": "Your report",
},
)WordPress / WooCommerce
add_action("woocommerce_order_status_processing", function($order_id){
$order = wc_get_order($order_id);
wp_remote_post("https://wati.pro/api/public/v1/send", [
"headers" => [
"Authorization" => "Bearer " . WATI_KEY,
"Content-Type" => "application/json",
],
"body" => wp_json_encode([
"to" => $order->get_billing_phone(),
"message" => "Order #" . $order_id . " confirmed. Thank you!",
]),
]);
});Ready to integrate?
Create your account, link a device, and grab an API key in under 5 minutes.