WAJIVAN

WhatsApp API

Send WhatsApp messages from your own software and receive replies and delivery updates, through your own WhatsApp Business number.तुमच्या स्वतःच्या WhatsApp Business नंबरवरून तुमच्या software मधून संदेश पाठवा आणि उत्तरं व delivery स्थिती मिळवा.

Quick start

  1. Connect your WhatsApp number in the WAJIVAN panel (Settings → Connect WhatsApp).
  2. Open the API tab and create a key. It is shown once.
  3. Call the API:
curl https://crm.rajeshchavan.org/api/public/v1/account \
  -H "Authorization: Bearer wj_live_…"

curl -X POST https://crm.rajeshchavan.org/api/public/v1/messages \
  -H "Authorization: Bearer wj_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1042-confirmation" \
  -d '{"to":"919800000001","type":"template","template":{"name":"hello_world","language":"en_US"}}'

Success returns 202 with the WhatsApp message id: {"id":"wamid.…","to":"919800000001","type":"template","status":"accepted","client_ref":null}. Add "dry_run": true to check a request without sending anything.

Authentication

Send your key in every request: Authorization: Bearer wj_live_…. API access is part of the Sanjivani 999 and 1999 plans. Keys can be revoked any time in the panel; a revoked key stops working immediately. Never put a key in a web page or mobile app — call the API from your server.

Each key has a rate limit (default 60 requests per minute). Over the limit you get 429 with a Retry-After header.

Endpoints

Base URL: https://crm.rajeshchavan.org/api/public/v1

GET/account

Your workspace, plan and the connected WhatsApp number (quality, status).

Scope: account:read

POST/messages

Send a text, template, image or document. Free-form types need the customer to have written in the last 24 hours; otherwise send a template. Add Idempotency-Key to make retries safe; add dry_run:true to validate without sending.

Scope: messages:send

{
  "to": "919800000001",
  "type": "template",
  "template": {
    "name": "hello_world",
    "language": "en_US"
  },
  "client_ref": "order-1042"
}

GET/messages

List stored messages, newest first. Page with next_before.

Scope: messages:read

  • contact — Only this phone number
  • direction — in or out
  • since — ISO time, inclusive
  • before — ISO time, exclusive (use next_before from the last page)
  • limit — 1–100, default 50

GET/messages/{id}

One message by its WhatsApp message id, with its delivery status.

Scope: messages:read

GET/templates

List your WhatsApp templates and their approval status.

Scope: templates:read

  • status — APPROVED, PENDING, REJECTED, PAUSED or DISABLED
  • name — Exact template name
  • limit — 1–100, default 50

POST/templates

Create a template. It is sent to WhatsApp for approval (status PENDING).

Scope: templates:write

{
  "name": "order_update",
  "language": "en_US",
  "category": "UTILITY",
  "components": [
    {
      "type": "BODY",
      "text": "Hi {{1}}, your order {{2}} has shipped.",
      "example": {
        "body_text": [
          [
            "Asha",
            "1042"
          ]
        ]
      }
    }
  ]
}

GET/templates/{name}

Every language/version of one template.

Scope: templates:read

Message types for POST /messages: text (text), template (template.name, template.language, optional template.components), image and document (link https URL, optional caption, filename). client_ref (up to 64 characters) is echoed back in webhooks so you can match your own records. Phone numbers: digits with country code; a 10-digit Indian mobile gets 91 added; Devanagari digits are converted.

Webhooks

Set a webhook URL (https, a domain name) in the API tab. We send a POST with a JSON body for each event. Reply with any 2xx within 8 seconds. Otherwise we retry after 1 min, 5 min, 30 min, 2 h and 6 h, then mark it failed (you can retry it from the panel).

Events: message.received (a customer wrote to you), message.status (sent, delivered, read, failed for messages you sent), ping (the test button).

{
  "id": "5b1c…",                    // delivery id (also in X-Wajivan-Delivery)
  "type": "message.received",
  "created": "2026-10-10T12:00:00.000Z",
  "data": {
    "id": "wamid.HBgM…", "contact": "919800000001", "name": "Asha",
    "type": "text", "text": "नमस्कार", "media": null,
    "timestamp": "2026-10-10T11:59:58.000Z"
  }
}
{
  "id": "9e2a…", "type": "message.status", "created": "2026-10-10T12:00:03.000Z",
  "data": { "id": "wamid.HBgM…", "contact": "919800000001", "status": "delivered",
            "error": null, "client_ref": "order-1042", "timestamp": "2026-10-10T12:00:02.000Z" }
}

Headers: X-Wajivan-Event, X-Wajivan-Delivery (use it to ignore duplicates) and X-Wajivan-Signature: t=<unix>,v1=<hex> — the HMAC-SHA256 of t + "." + raw body with your webhook secret. Always verify it and reject timestamps older than 5 minutes:

import crypto from "node:crypto";

// rawBody must be the exact bytes you received (before JSON.parse).
function verify(secret, header, rawBody, toleranceSec = 300) {
  const m = /^t=(\d+),v1=([a-f0-9]{64})$/.exec(header || "");
  if (!m) return false;
  if (Math.abs(Date.now() / 1000 - Number(m[1])) > toleranceSec) return false; // replay protection
  const expected = crypto.createHmac("sha256", secret).update(m[1] + "." + rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(m[2]));
}
import hmac, hashlib, re, time

def verify(secret: str, header: str, raw_body: bytes, tolerance=300) -> bool:
    m = re.fullmatch(r"t=(\d+),v1=([a-f0-9]{64})", header or "")
    if not m or abs(time.time() - int(m[1])) > tolerance:
        return False
    expected = hmac.new(secret.encode(), m[1].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, m[2])

Errors

Errors share one shape: {"error":{"code":"template_required","message":"…"}}.

HTTPcodeMeaning
400invalid_request / invalid_json / invalid_phoneThe request is malformed. `details` lists the fields.
401unauthorizedMissing, malformed, unknown or revoked key.
403plan_required / forbidden / subscription_inactiveYour plan has no API, the key lacks the scope, or the subscription lapsed.
404not_foundNo such message or template in your workspace.
409whatsapp_not_connectedConnect a WhatsApp number in the CRM first.
413payload_too_largeBody larger than 64 KB.
422opted_out / template_required / undeliverable / template_error / idempotency_conflictThe contact opted out, the 24-hour window is closed, the number is not on WhatsApp, a template problem, or a reused Idempotency-Key with a different body.
429rate_limited / meta_rate_limitedSlow down. Honour the Retry-After header.
502upstream_error / whatsapp_auth_errorWhatsApp did not accept the call or the connection must be re-authorised.

WhatsApp rules you must follow

WAJIVAN API v1 · Terms · Privacy