Aumfig API logo
Aumfig API
Back to home Sign in

API Documentation

Send WhatsApp messages from your code — plain REST + JSON. Every endpoint returns JSON, accepts JSON, and uses standard HTTP status codes.
📖 For AI coding tools: Share /llm-api-reference.txt with ChatGPT, Claude, Cursor, or any AI — it's a clean plain-text reference with all endpoints, parameters, error codes, and ready-to-use code examples (cURL, JavaScript, Python, PHP, Google Apps Script). The AI will write integration code for you instantly.

Authentication

Every API request must include your API key in the x-api-key header. Get your key after creating an account — it appears in Settings → API Key.

x-api-key: wag_sk_YOUR_API_KEY_HERE

Base URL

All endpoints below are relative to:

https://your-domain.com/api/v1

Replace with the host where the gateway is deployed. After logging in, your exact base URL is shown on the dashboard.

Picking a phone (slots)

One API key authenticates your account. You can connect up to two WhatsApp numbers ("slots"). To choose which one sends, add a slot field to the request body:

// Send from Phone 2 (Pro) { "phone": "919876543210", "message": "Hi", "slot": "b" } // Response always echoes which slot + the actual sender number { "success": true, "slot": "b", "from_phone": "91XXXXXXXXXX" }

Quick start

Hello world in three popular languages. Replace YOUR_API_KEY and the destination phone number.

curl -X POST "https://your-domain.com/api/v1/send-message" \ -H "Content-Type: application/json" \ -H "x-api-key: YOUR_API_KEY" \ -d '{"phone":"919876543210","message":"Hello from API!"}'
// Node.js (fetch is built-in from v18+; in browsers it's native too) const res = await fetch('https://your-domain.com/api/v1/send-message', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': 'YOUR_API_KEY' }, body: JSON.stringify({ phone: '919876543210', message: 'Hello from API!' }) }); const data = await res.json(); console.log(data);
import requests res = requests.post( 'https://your-domain.com/api/v1/send-message', headers={ 'Content-Type': 'application/json', 'x-api-key': 'YOUR_API_KEY' }, json={ 'phone': '919876543210', 'message': 'Hello from API!' } ) print(res.json())
<?php $ch = curl_init('https://your-domain.com/api/v1/send-message'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'x-api-key: YOUR_API_KEY' ], CURLOPT_POSTFIELDS => json_encode([ 'phone' => '919876543210', 'message' => 'Hello from API!' ]) ]); $response = curl_exec($ch); curl_close($ch); echo $response;

All endpoints

POST/send-messageText or media to contacts, groups, or multiple targets — one endpoint for everything
POST/send-bulkUp to 500 contacts with {{name}} merge variables
POST/broadcastSame message to up to 100 groups and/or saved contacts
GET/contactsList saved contacts (Basic 100 · Pro 200 · Business 500)
POST/contactsSave one contact
POST/contacts/bulkImport many contacts at once
PUT/contacts/:idEdit a saved contact
DELETE/contacts/:idRemove a saved contact
POST/check-whatsappCheck if numbers are on WhatsApp
POST/schedule-messageSend at a future timestamp
GET/statusConnection state for all slots + counts
GET/groupsList groups you belong to
GET/messagesRecent send history + stats
GET/queueCurrent bulk send queue status
GET/scheduledPending scheduled messages
GET/subscriptionYour plan + usage limits
GET/webhookYour outgoing webhook config
PUT/webhookUpdate outgoing webhook URL
POST/wh/in/<trigger_token>No-auth trigger URL for external services

POST /send-message

The single unified endpoint for sending anything — text, images, documents, videos — to any combination of contacts and groups.

FieldTypeRequiredDescription
phonestring | string[]RequiredPhone number (919876543210), group ID (120363xxx@g.us), or an array of both
messagestringFor textText body. Max 4096 chars. Supports emoji, line breaks (\n), WhatsApp markdown.
typestringFor mediaimage, document, pdf, or video
media_urlstringFor mediaPublic URL of the file (must be reachable from the server)
media_base64stringFor mediaBase64-encoded file (alternative to media_url). Max ~1.5MB.
filenamestringOptionalDisplay filename (recommended for documents/PDFs)
captionstringOptionalCaption shown under the media
slotstringOptional"a" (default), "b", "c", "d", "e" — based on your plan
// ─── TEXT EXAMPLES ─── // Text to one contact { "phone": "919876543210", "message": "Hello!" } // Text to a group { "phone": "120363025123456789@g.us", "message": "Hi everyone!" } // Text to multiple targets (contacts + groups) { "phone": ["919876543210", "918765432109", "120363025123456789@g.us"], "message": "Hello all!" } // ─── MEDIA EXAMPLES ─── // Image to one contact { "phone": "919876543210", "type": "image", "media_url": "https://example.com/photo.jpg", "caption": "Check this out!" } // PDF to a group { "phone": "120363025123456789@g.us", "type": "pdf", "media_url": "https://example.com/invoice.pdf", "filename": "invoice.pdf" } // Image to multiple targets { "phone": ["919876543210", "918765432109", "120363025123456789@g.us"], "type": "image", "media_url": "https://example.com/photo.jpg" } // Response (200) { "success": true, "queued": 3, "targets": ["919876543210", "918765432109", "120363025123456789@g.us"], "slot": "a", "from_phone": "91XXXXXXXXXX" }

POST /send-bulk

Send to up to 500 contacts in one call. Use {{name}} and {{phone}} as merge variables. The server enforces a 25s delay between messages plus a 2-minute rest every 20 sends to avoid bans. Trial plan blocked.

FieldTypeRequiredDescription
contactsarrayRequiredUp to 500 objects, each with phone and optional name
messagestringRequiredTemplate. Use {{name}} and {{phone}} for substitution.
slotstringOptional"a" or "b"
{ "contacts": [ { "phone": "919876543210", "name": "Rahul" }, { "phone": "918765432109", "name": "Priya" } ], "message": "Hi {{name}}, your order is ready!", "slot": "a" } // Response — queued for sending { "success": true, "queued": 2, "estimated_time_sec": 50 }

POST /broadcast Pro

Send the same message to a mix of groups and individual contacts, up to 100 recipients per request. Recipients can come from three sources, used together or separately. Duplicates across sources are removed automatically. Trial plan blocked.

FieldTypeRequiredDescription
messagestringRequiredText to broadcast. Max 4096 chars.
groupsarrayOne ofList of { id, name? } — group JIDs (with or without @g.us)
contactsarrayOne ofList of { phone, name? } — raw numbers, 10–15 digits, no +
contact_idsarrayOne ofList of saved-contact ids from /contacts (e.g. "ct_a1b2c3")
slotstringOptional"a" or "b"
// Mix groups + raw numbers + saved-contact ids in a single broadcast { "message": "Diwali sale starts tonight!", "groups": [ { "id": "120363025111111111@g.us", "name": "VIP Customers" } ], "contacts": [ { "phone": "919876543210", "name": "Rahul" } ], "contact_ids": ["ct_a1b2c3d4e5f6", "ct_f6e5d4c3b2a1"] } // Response — queued for sending { "success": true, "queued": 4, "breakdown": { "groups": 1, "contacts": 3 }, "estimated_time_sec": 110 }
Tip: Pre-save your VIP list with POST /contacts/bulk, then trigger broadcasts using contact_ids — no need to re-send phone numbers each time.

POST /check-whatsapp

Check whether one or more numbers are registered on WhatsApp before sending.

// Request { "phones": ["919876543210", "918765432109"] } // Response { "success": true, "results": [ { "phone": "919876543210", "exists": true }, { "phone": "918765432109", "exists": false } ] }

POST /schedule-message

Queue a message to send at a specific future time. Returns a scheduled job ID.

FieldTypeRequiredDescription
phonestringRequiredRecipient phone (or use group_id)
messagestringRequiredText to send
sendAtstringRequiredISO 8601 timestamp, e.g. "2026-06-15T14:30:00Z"
slotstringOptional"a" or "b"

GET /status

Returns connection state for both phone slots plus daily/total send counts.

{ "connected": true, "max_sessions": 2, "slots": { "a": { "connected": true, "phone_number": "91XXXXXXXXXX" }, "b": { "connected": true, "phone_number": "91YYYYYYYYYY" } }, "daily_sent": 42, "total_sent": 12500 }

GET /groups

List WhatsApp groups your account belongs to. Use ?slot=a|b to choose which phone.

{ "success": true, "groups": [ { "id": "120363025...@g.us", "name": "Team Chat", "participants": 12 } ] }

GET /messages

Recent send history. Query parameters: type (single, group, broadcast), status (sent, failed, queued), limit (default 50, max 500). Each row includes slot and slotPhone.

GET /queue

Returns the current bulk send queue — items pending, in progress, and processing rate. Useful for status bars.

GET /scheduled

Returns all your pending scheduled messages. Delete with DELETE /scheduled/:id.

GET /subscription

Returns your active plan, expiry date, daily message limit, and whether bulk/Pro features are unlocked.

Message Templates

Save reusable message snippets with {{variable}} placeholders, and fill them in at send time. Useful for order confirmations, OTP messages, reminders, and any text you reuse with small per-recipient changes.

Pro only: Creating and editing templates requires the Pro plan. Listing, deleting, and the /use counter work on all plans. Max 50 templates per account, 60 chars per name, 4096 chars per body.

Template variables follow the pattern {{variable_name}} — alphanumeric and underscores only. The API automatically extracts variable names from your content into a variables array. You then substitute them client-side before calling /send-message:

// Template body "Hi {{name}}, your order #{{order_id}} is on the way!" // → API extracts: variables = ["name", "order_id"] // → You substitute and send via /send-message

GET /templates

List all your saved templates, newest first. Available on every plan.

// Response { "success": true, "templates": [ { "id": "tpl_a3f9b2c1d4", "name": "Order Confirmation", "content": "Hi {{name}}, your order #{{order_id}} is confirmed.", "category": "transactional", "variables": ["name", "order_id"], "usageCount": 42, "createdAt": "2026-04-12T10:00:00Z", "updatedAt": "2026-05-01T11:24:00Z" } ] }

POST /templates Pro

Create a new template. Variables are auto-extracted from content.

FieldTypeRequiredDescription
namestringRequiredHuman-readable name (max 60 chars)
contentstringRequiredBody with optional {{variable}} placeholders (max 4096 chars)
categorystringOptionalTag for grouping in the dashboard (max 30 chars)
curl -X POST "https://your-domain.com/api/v1/templates" \ -H "Content-Type: application/json" \ -H "x-api-key: YOUR_API_KEY" \ -d '{ "name": "Order Confirmation", "content": "Hi {{name}}, your order #{{order_id}} is confirmed.", "category": "transactional" }'

PUT /templates/:id Pro

Update an existing template. Pass any subset of name, content, category. Variables re-extract on every content change.

curl -X PUT "https://your-domain.com/api/v1/templates/tpl_a3f9b2c1d4" \ -H "Content-Type: application/json" \ -H "x-api-key: YOUR_API_KEY" \ -d '{"content": "Hi {{name}}, your order is out for delivery!"}'

DELETE /templates/:id

Delete a template permanently. Returns 404 if the template ID does not exist.

curl -X DELETE "https://your-domain.com/api/v1/templates/tpl_a3f9b2c1d4" \ -H "x-api-key: YOUR_API_KEY"

POST /templates/:id/use

Increment the usageCount for a template — optional telemetry call that lets you sort/rank templates by popularity in your own UI. Returns the updated template.

// JavaScript example — bump usage when you actually use a template async function sendFromTemplate(templateId, recipient, values) { const tpls = await (await fetch('/api/v1/templates', { headers: { 'x-api-key': API_KEY } })).json(); const tpl = tpls.templates.find(t => t.id === templateId); // substitute {{variable}} placeholders let body = tpl.content; for (const [k, v] of Object.entries(values)) { body = body.replace(new RegExp('\\{\\{\\s*' + k + '\\s*\\}\\}', 'g'), v); } // send and bump usage counter (fire-and-forget) await fetch('/api/v1/send-message', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': API_KEY }, body: JSON.stringify({ phone: recipient, message: body }) }); fetch('/api/v1/templates/' + templateId + '/use', { method: 'POST', headers: { 'x-api-key': API_KEY } }); }

Contact book

A per-account address book of WhatsApp recipients so you don't re-type numbers each time. Saved contacts can be referenced by id in the /broadcast endpoint via contact_ids.

Plan caps: Trial = 20 contacts · Basic = 100 contacts · Pro = 300 contacts. Phone numbers are stored in canonical form (digits only, no +, 10–15 digits). Duplicates are rejected.

GET /contacts

List your saved contacts, newest first. Optional ?search= matches name, phone, or any tag (case-insensitive). Optional ?limit= caps the response.

// Response { "success": true, "contacts": [ { "id": "ct_a1b2c3d4e5f6", "name": "Rahul Kumar", "phone": "919876543210", "notes": "VIP customer", "tags": ["vip", "mumbai"], "createdAt": "2026-05-18T09:00:00Z", "updatedAt": "2026-05-18T09:00:00Z" } ], "count": 1, "total_stored": 14, "limit": 300, "tier": "pro" }

POST /contacts

Save a single contact. Returns 403 CONTACTS_CAP_REACHED when your plan's contact cap is full. Returns 409 DUPLICATE_PHONE if another contact already uses the number.

FieldTypeRequiredDescription
phonestringRequiredCountry code + number (10–15 digits). Non-digits are stripped — "+91 98765 43210" works.
namestringOptionalDisplay name (max 80 chars)
notesstringOptionalFree-form note (max 200 chars)
tagsarrayOptionalUp to 8 string tags, max 30 chars each
curl -X POST "https://your-domain.com/api/v1/contacts" \ -H "Content-Type: application/json" \ -H "x-api-key: YOUR_API_KEY" \ -d '{ "name": "Rahul Kumar", "phone": "919876543210", "tags": ["vip", "mumbai"], "notes": "Repeat buyer" }'

POST /contacts/bulk

Import many contacts in one request — perfect for migrating from a CSV or spreadsheet. Up to 1000 rows per call. The endpoint inserts what it can, then returns a per-row outcome explaining why anything got skipped.

Skip reasons:

// Request { "contacts": [ { "name": "Rahul", "phone": "919876543210" }, { "name": "Priya", "phone": "918765432109", "tags": ["wholesale"] }, { "name": "Bad", "phone": "123" } ] } // Response { "success": true, "added": [ { "id": "ct_...", "name": "Rahul", "phone": "919876543210" }, { "id": "ct_...", "name": "Priya", "phone": "918765432109" } ], "skipped": [ { "index": 2, "phone": "123", "reason": "invalid_phone" } ], "addedCount": 2, "skippedCount": 1, "currentTotal": 52, "limit": 300 }

PUT /contacts/:id

Edit a contact. Pass any subset of name, phone, notes, tags. Changing phone to one another contact already has returns 409 DUPLICATE_PHONE.

curl -X PUT "https://your-domain.com/api/v1/contacts/ct_a1b2c3d4e5f6" \ -H "Content-Type: application/json" \ -H "x-api-key: YOUR_API_KEY" \ -d '{"tags": ["vip", "loyal"], "notes": "Hi-value account"}'

DELETE /contacts/:id

Remove a single contact. To remove many at once, POST a list of ids to /contacts/delete-many:

// Single curl -X DELETE "https://your-domain.com/api/v1/contacts/ct_a1b2c3" \ -H "x-api-key: YOUR_API_KEY" // Many at once curl -X POST "https://your-domain.com/api/v1/contacts/delete-many" \ -H "Content-Type: application/json" \ -H "x-api-key: YOUR_API_KEY" \ -d '{"ids":["ct_a1b2c3","ct_d4e5f6"]}'

Outgoing webhooks

Configure a URL to receive delivery events (sent / failed) for every message your account sends.

// GET /webhook — view current config { "url": "https://yourapp.com/wa-events", "enabled": true } // PUT /webhook — update { "url": "https://yourapp.com/wa-events", "enabled": true } // POST /webhook/test — send a test ping // Payload your endpoint receives { "event": "message.sent", "messageId": "msg_abc123", "to": "919876543210", "slot": "a", "slotPhone": "91XXXXXXXXXX", "timestamp": "2026-05-16T10:30:00Z" }

Trigger token (no-auth URL)

Each account has a secret trigger token that creates a public URL services like Google Forms, Zapier, Make, or Apps Script can POST to without sending an API key. Useful when the calling service can't add custom headers.

⚠ Treat the trigger token like a password. Anyone with the URL can send messages from your account. Regenerate it from the dashboard if leaked.
// POST to your trigger URL — no x-api-key needed curl -X POST "https://your-domain.com/wh/in/tt_YOUR_TRIGGER_TOKEN" \ -H "Content-Type: application/json" \ -d '{"phone":"919876543210","message":"Hi from form!"}' // Same body as /send-message — phone, message, optional slot

Error responses

All errors return a JSON body with an error string and (when useful) a machine-readable code.

StatuscodeMeaning
400Bad request body — see error for details
401Missing or invalid x-api-key
402NO_SUBSCRIPTIONNo active subscription — start a trial or pay
403PRO_REQUIREDUsed slot: "b" without Pro plan
403TRIAL_BULK_BLOCKEDBulk/broadcast not available on trial plan
403SEND_WINDOW_CLOSEDOutside your configured send window — response includes nextOpenAt
400BROADCAST_CAP_EXCEEDEDSent more than 100 recipients in one broadcast — split into multiple calls
400NO_RECIPIENTSBroadcast call with no groups, contacts, or contact_ids
403CONTACTS_CAP_REACHEDYour contact book is full for your plan (Trial 20 / Basic 100 / Pro 300)
409DUPLICATE_PHONETried to save a contact whose phone already exists
429DAILY_LIMIT_REACHEDHit trial-plan daily limit. Response includes limit and used.
429Per-IP rate limit (60 requests/min on /api/*)
500Server error — retry, then contact support if it persists
// Example error response { "error": "Daily message limit of 100 reached. Upgrade to a paid plan to remove the limit.", "code": "DAILY_LIMIT_REACHED", "limit": 100, "used": 100 }

Rate limits & throttling

Need help?

Found an issue or have a question? Email support@aumfig.com or open the dashboard chat after signing in.

© 2026 Aumfig API · Home · Sign in · Create account