Limited this week15-min demo = Rs 500 voucher + 200 credits Book demo
Back to Platform
Developer-First Infrastructure

WAutomate
API V1 Reference.

"The most stable WhatsApp engine for high-scale enterprise integrations."

Getting Started

To begin integrating with the WAutomate REST API, you must first obtain an **API Key** from your dashboard. Our API follows REST principles and returns JSON-encoded responses.

Base URL
https://wautomate.in/api/v1

Prepend this to every endpoint path shown below — e.g. POST https://wautomate.in/api/v1/send-template.

How to get your API Key:

  1. 1

    Login to Dashboard

    Navigate to your dashboard at wautomate.in/login

  2. 2

    Go to Settings

    Click on the **Settings** icon in the sidebar and select the **API Keys** tab.

  3. 3

    Generate Key

    Click "Generate New Key." Copy your key and store it securely. We will not show it again for security reasons.

Authentication

"WAutomate uses Bearer Token authentication to secure every request. Ensure your key is kept private."

Required Header:
Authorization: Bearer ra_live_...
Send these too:
Content-Type: application/json
Accept: application/json

We return JSON either way, so Accept is belt and braces — but send it. Some HTTP clients follow redirects by default (curl --location), and a redirect turns a POST into a GET, which is how a request ends up somewhere it was never meant to go.

Send Template

HIGH PRIORITY

Trigger pre-approved Meta templates for order confirmations, OTPs, or notifications. Supports media headers (Image, Video, Document).

POST
/api/v1/send-template

Required Fields

  • phone: Target number (e.g. 919876543210)
  • template: Meta template name
  • language: The template's language code — en or en_US. It must match the template exactly; most templates here are en_US.

Media Headers (Optional)

  • header_media_type: image | video | document
  • header_media_url: Publicly accessible URL
  • filename: Custom name for documents
Example JSON Payload:
{
  "phone": "919876543210",
  "template": "invoice_paid",
  "language": "en_US",
  "variables": ["INV-2024-001", "March 2024"],
  "header_media_type": "document",
  "header_media_url": "https://example.com/invoices/INV-001.pdf",
  "filename": "Invoice_INV001.pdf"
}

Trigger Campaign

NEW

Trigger a pre-defined marketing campaign for a specific contact. Useful for abandoned cart recovery or CRM triggers.

POST
/api/v1/send-campaign
{
  "phone": "919876543210",
  "campaign_id": "782"
}

Create with CSV

BULK API

Upload a CSV file to trigger a high-volume marketing or utility campaign. Our engine handles contact creation and variable mapping automatically.

POST
/api/v1/campaigns/create-with-csv

Required Form-Data

  • name: String (Internal name)
  • template_id: Integer (Template ID)
  • file: .csv (Max 10MB)

CSV Column Headers

  • phone: Target number (Required)
  • var_1, var_2: Template variables
Example cURL:
curl -X POST https://wautomate.in/api/v1/campaigns/create-with-csv \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "name=API_Campaign_001" \
  -F "template_id=12" \
  -F "file=@/path/to/your/contacts.csv"

Send a Message

UPDATED

Text, images, video, documents, audio and reply buttons — the same things a chatbot flow can send. Only deliverable inside an open 24-hour customer service window; outside it, use Send Template.

POST
/api/v1/send-message

Fields

  • phone: recipient, e.g. 919876543210 (required)
  • message: the text. Required unless you are sending media, where the words go in caption instead.
  • type: text | image | video | document | audio (default text)
  • media_url or media_id: where the file is. Use media_id from Media Upload, or any public URL.
  • caption: text shown under an image, video or document.
  • filename: the name the recipient sees for a document.
  • buttons: up to 3 reply buttons, each {id, title}. Text messages only.
Plain text
{
  "phone": "919876543210",
  "message": "Hello! Your agent is now online."
}
Image
{
  "phone": "919876543210",
  "type": "image",
  "media_url": "https://yoursite.in/order.jpg",
  "caption": "Your order has shipped 🚚"
}
Document (PDF)
{
  "phone": "919876543210",
  "type": "document",
  "media_id": "1234567890",
  "filename": "Invoice_INV-2231.pdf",
  "caption": "Your invoice is attached."
}
Video
{
  "phone": "919876543210",
  "type": "video",
  "media_url": "https://yoursite.in/demo.mp4",
  "caption": "60-second walkthrough"
}
Reply buttons (max 3, title ≤ 20 chars)
{
  "phone": "919876543210",
  "message": "Did that solve your problem?",
  "buttons": [
    { "id": "yes",   "title": "Yes, thanks" },
    { "id": "no",    "title": "Not really" },
    { "id": "agent", "title": "Talk to a person" }
  ]
}

When the customer taps one, the id comes back on your message.inbound webhook — that is how you know which button was pressed.

Upload once, then send — full cURL
# 1. Upload the file (returns a media_id, valid 30 days)
curl -X POST https://wautomate.in/api/v1/public/media \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@invoice.pdf"

# 2. Send it — no public URL needed
curl -X POST https://wautomate.in/api/v1/send-message \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: invoice-INV-2231" \
  -H "Content-Type: application/json" \
  -d '{"phone":"919876543210","type":"document","media_id":"1234567890","filename":"Invoice.pdf"}'

Reply buttons cannot be attached to a media message — that is a WhatsApp limit, and the API returns 422 rather than dropping them silently. Send the media first, then a text message with the buttons.

Check Message Status

NEW

Poll the delivery status of any message returned by Send Message or Send Template. Pass the message_id you received on send. When a message fails, the response includes a human-readable error reason so you can react in your own system.

GET
/api/v1/message-status/{message_id}

Possible Status Values

  • queued: Accepted and waiting in the send queue
  • sent: Handed off to WhatsApp
  • delivered: Delivered to the recipient's device
  • read: Read by the recipient
  • failed: Not sent — see the error field for the reason
Example cURL:
curl https://wautomate.in/api/v1/message-status/123456 \
  -H "Authorization: Bearer YOUR_API_KEY"
Success Response:
{
  "success": true,
  "message_id": 123456,
  "status": "delivered",
  "delivered": true,
  "timestamp": "2026-09-02T12:34:56Z"
}
Failed Response:
{
  "success": true,
  "message_id": 123456,
  "status": "failed",
  "delivered": false,
  "error": "Insufficient points to send message."
}

Rate Limits, Errors & Pagination

These rules apply to every endpoint on this page.

Rate Limits

  • Limits are counted per API key, per minute.
  • The default for a new key is 60 requests/minute. Higher limits are set per key — ask support.
  • Over the limit you get 429 with a Retry-After header. Back off and retry; do not hammer.

Pagination

  • List endpoints accept ?page= and ?per_page=.
  • Default page size is 50; the maximum is 200.
  • Every list response carries a pagination object with page, per_page, total and total_pages.

HTTP Status Codes

CodeWhat it means
200 / 201Success.
400Malformed request — for example a WhatsApp number is not connected.
401Missing, invalid or suspended API key.
402Not enough wallet balance to complete the request.
403Your plan or key does not include this capability.
404Not found — or it belongs to another account.
409Conflict — a duplicate, or a request with the same Idempotency-Key is still running.
422Validation failed. The response names the fields.
429Rate limited.
5xxOur side. Safe to retry with the same Idempotency-Key.
Error Shape:
{
  "success": false,
  "error": "Invalid or suspended API key."
}

Idempotency

NEW

Every send debits your wallet. If a request times out you cannot safely retry it without risking a second charge — so send an Idempotency-Key header and we will return the first response instead of doing the work twice.

How it works

  • Generate a unique key per logical operation — a UUID, or your own order ID.
  • Send it as Idempotency-Key: <your-key> on any POST.
  • A retry with the same key returns the original response plus Idempotency-Replayed: true.
  • Keys are remembered for 24 hours and are scoped to your API key.
  • Reusing a key with a different body returns 422 — that is a bug in the caller, not a retry.
  • Only successful responses are replayed. A 4xx or 5xx can be retried normally.

The header is optional and existing integrations are unaffected — but for anything that spends money, treat it as required.

Example cURL:
curl -X POST https://wautomate.in/api/v1/send-template \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: order-2026-09-15-8842" \
  -H "Content-Type: application/json" \
  -d '{"phone":"919876543210","template":"invoice_paid","language":"en_US"}'

Balance & Usage

NEW

Billing is usage-based, so check your balance before starting a large run and reconcile spend afterwards — without opening the dashboard.

GET
/api/v1/public/balance
Response:
{
  "success": true,
  "balance": 4820.75,
  "currency": "INR",
  "low_balance_threshold": 500,
  "is_low": false
}
GET
/api/v1/public/usage?from=2026-09-01&to=2026-09-30

Both dates are optional — the default window is the current month, which is what an invoice covers.

Response:
{
  "success": true,
  "from": "2026-09-01T00:00:00+05:30",
  "to": "2026-09-30T23:59:59+05:30",
  "currency": "INR",
  "billed_messages": 18422,
  "spend": 14556.32,
  "refunded": 212.40,
  "net_spend": 14343.92,
  "by_conversation_type": {
    "marketing":      { "messages": 12010, "amount": 10568.80 },
    "utility":        { "messages": 5900,  "amount": 3540.00 },
    "authentication": { "messages": 512,   "amount": 447.52 }
  }
}

Contacts

NEW

Read, update and delete contacts, and write the custom attributes your chatbot flows read as {{attr.your_key}}. This is what keeps your CRM and WAutomate in step.

GET
/api/v1/public/contactsList contacts. Filter with ?search=, ?phone= or ?tag=.
GET
/api/v1/public/contacts/{id}One contact, with all its attributes.
PUT
/api/v1/public/contacts/{id}Update name, phone or opt_in.
DELETE
/api/v1/public/contacts/{id}Soft-delete a contact.
GET
/api/v1/public/contacts/{id}/attributesRead every custom attribute.
PUT
/api/v1/public/contacts/{id}/attributesMerge attributes in — existing keys are kept.
DELETE
/api/v1/public/contacts/{id}/attributes/{key}Remove one attribute.
Writing attributes a flow can read:
curl -X PUT https://wautomate.in/api/v1/public/contacts/4821/attributes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"attributes":{"Order ID":"RA-9001","plan":"gold","amount":4999}}'

# Keys are normalised the same way the flow engine normalises them:
# "Order ID" is stored as order_id and read in a flow as {{attr.order_id}}.

Templates

NEW

Submit templates to Meta and track their approval status programmatically — necessary once you are managing more accounts than you want to click through.

GET
/api/v1/public/templatesList your templates. Add ?all=1 to include pending and rejected ones.
POST
/api/v1/public/templatesSubmit a new template to Meta. Returns it with status PENDING.
POST
/api/v1/public/templates/syncPull the latest approval status from Meta.
DELETE
/api/v1/public/templates/{id}Delete a template from Meta and from your account.
Create a template:
curl -X POST https://wautomate.in/api/v1/public/templates \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "name=order_shipped" \
  -F "category=UTILITY" \
  -F "language=en" \
  -F 'components=[{"type":"BODY","text":"Hi {{1}}, order {{2}} has shipped."}]'

Media Upload

NEW

Upload an image, video or PDF and get back a media_id you can use in a template header — no public URL needed.

POST
/api/v1/public/mediamultipart/form-data, field file, max 100 MB
Example:
curl -X POST https://wautomate.in/api/v1/public/media \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@invoice.pdf"

{ "success": true, "media_id": "1234567890", "media_url": null, "expires_in_days": 30 }

A Meta media id expires after 30 days. On Gupshup-hosted numbers you get a media_url instead of a media_id — use whichever field is non-null.

Campaign Results

NEW

You could always create and trigger a campaign — now you can read the outcome without polling every message id separately.

GET
/api/v1/public/campaignsList campaigns. Filter with ?status=.
GET
/api/v1/public/campaigns/{id}/statsSent, delivered, read and failed counts.
Response:
{
  "success": true,
  "campaign_id": 331,
  "name": "Diwali offer",
  "status": "COMPLETED",
  "stats": { "sent": 4120, "delivered": 3988, "read": 2411, "failed": 132 },
  "created_at": "2026-09-14T09:12:00+05:30"
}

Webhooks

HIGH PRIORITY

Polling is the slow way to find out what happened. Point us at a URL and we will POST to it the moment a customer replies, a message is delivered or a deal moves. Configure endpoints under Settings → Webhooks in your dashboard.

Events

message.inbound — A customer sent you a message.
message.delivered — A message you sent reached the device.
contact.created — A new contact was added.
contact.updated — A contact was changed.
lead.created — A lead came in from a form or landing page.
deal.created — A deal was opened.
deal.updated — A deal changed stage or value.
deal.won — A deal was marked won.
deal.lost — A deal was marked lost.

Subscribe to * to receive every event, including ones added later.

Delivery Payload:
POST https://your-app.example.com/hooks/wautomate
Content-Type: application/json
X-WAutomate-Event: message.inbound
X-WAutomate-Delivery-Id: 90218
X-WAutomate-Signature: sha256=8f4e...c1a9
User-Agent: WAutomate-Webhooks/1.0

{
  "event": "message.inbound",
  "delivery_id": 90218,
  "tenant_id": 4,
  "fired_at": "2026-09-15T11:02:41+05:30",
  "data": { "...": "event-specific fields" }
}

Verifying the signature

X-WAutomate-Signature is sha256= followed by the HMAC-SHA256 of the raw request body, keyed with your webhook secret. Compare it in constant time and reject anything that does not match.

// Node
const expected = 'sha256=' + crypto
  .createHmac('sha256', SECRET)
  .update(rawBody)          // the raw bytes, not JSON.parse'd
  .digest('hex');

crypto.timingSafeEqual(
  Buffer.from(expected),
  Buffer.from(req.header('X-WAutomate-Signature'))
);

Retries & Reliability

  • Reply 2xx within 10 seconds. Anything else counts as a failure.
  • We retry up to 5 times, backing off 10s → 30s → 90s → 270s → 810s.
  • Do the real work in a background job and return 2xx immediately — a slow handler becomes a retry storm.
  • A retry repeats the same delivery_id. De-duplicate on it, because at-least-once means you will see one twice.

Partner API

NEW

For resellers and agencies running WAutomate for their own clients: create each client's workspace, fund it from your balance and issue it an API key — all without touching our dashboard.

These endpoints need a key with partner provisioning enabled — an ordinary developer key gets 403. Contact support to have it turned on.

POST
/api/v1/public/partner/tenantsCreate a client workspace, its owner login and its API key.
GET
/api/v1/public/partner/tenantsList your client workspaces with their balances.
GET
/api/v1/public/partner/tenants/{id}One workspace.
POST
/api/v1/public/partner/tenants/{id}/creditsMove credits from your balance into theirs.
POST
/api/v1/public/partner/tenants/{id}/api-keysIssue another API key for that workspace.
POST
/api/v1/public/partner/tenants/{id}/suspendSuspend their keys — data is kept.
POST
/api/v1/public/partner/tenants/{id}/activateRe-enable them.
Provision a client:
curl -X POST https://wautomate.in/api/v1/public/partner/tenants \
  -H "Authorization: Bearer YOUR_PARTNER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "company_name": "Sharma Textiles",
    "owner_name": "Rohit Sharma",
    "owner_email": "rohit@sharmatextiles.in",
    "credits": 2000
  }'

{
  "success": true,
  "tenant": { "id": 412, "company_name": "Sharma Textiles", "balance": 2000, "currency": "INR" },
  "owner": { "id": 908, "name": "Rohit Sharma", "email": "rohit@sharmatextiles.in" },
  "api_key": "ra_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "owner_password": "generated-once-store-it"
}

The API key and the generated password are returned exactly once. Nothing stores them in readable form afterwards — save them when you receive them.

Ready to integrate?

Build your first WhatsApp integration in minutes with our robust SDKs.