Docs/

API keys & webhooks

Server-to-server

API keys & webhooks

Scoped API keys authenticate REST calls. Webhooks push the same events to your systems with HMAC-SHA256 signatures and automatic retries.

API key shape

Keys are created in Settings → Developer (OWNER or ADMIN only). The plaintext is shown exactly once at creation — WhautX stores only the SHA-256 hash and a 25-character display prefix.

whautx_live_ + 48 hex chars (~192 bits entropy)

Send it as Authorization: Bearer whautx_live_xxx. Revoked and expired keys fail at lookup time.

Scopes

Each endpoint requires at least one scope. A request to an endpoint whose scope your key lacks returns 403 Forbidden.

  • contacts.readGET /contacts, /contacts/:id, /conversations, conversation messages.
  • contacts.writePOST /contacts, PATCH /contacts/:id.
  • messages.sendPOST /messages (text inside the 24-hour window).
  • campaigns.readGET /campaigns.
  • campaigns.writePOST /campaigns — drafts only; start them from the app.
  • forms.submitPOST /forms/:formId/submit (server-to-server form ingestion).
  • knowledge.readGET /knowledge (metadata only — never raw embeddings).

Webhooks

Webhook subscriptions are independent of API keys. Register a URL and pick the events you want delivered. Each subscription gets its own signing secret — never reuse one across endpoints.

Event catalog

  • message.receivedInbound WhatsApp message from a contact.
  • message.deliveredOutbound message delivered (mirrored from Meta).
  • message.readOutbound message read by the recipient.
  • contact.createdA new contact was added to your workspace.
  • deal.stage_changedA deal moved between pipeline stages.
  • campaign.completedA campaign finished sending.

Payload shape

Every delivery is a versioned envelope — data varies by event, but event and deliveredAt are always present.

{
  "event": "message.received",
  "deliveredAt": "2026-07-18T12:34:56.789Z",
  "data": { /* event-specific */ }
}

Signature verification

Every delivery carries an X-WhautX-Signature: sha256=<hex> header. Compute it as HMAC-SHA256 of the raw body using the subscription secret, and compare with a constant-time equality check.

import crypto from "node:crypto";

export function verify(rawBody, signatureHeader, secret) {
  const expected = "sha256=" + crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Retry policy

  • Up to 3 attempts per delivery.
  • Exponential backoff: 30s, 60s, 120s.
  • Per-request timeout: 8 seconds.
  • Every attempt — including the final failure — is logged on a WebhookDelivery row you can inspect via the Developer page.