WaMCP

Webhooks

Receive real-time WhatsApp events on your server

Configure a webhook URL in Settings → Webhook to receive real-time events from your WhatsApp clients.

Envelope

Every event is delivered as a POST request with a JSON body in this shape:

{
  "type": "whatsapp" | "signup_completed" | "template_status" | "flow_completion" | "test",
  "client_id": "<uuid>",
  "external_id": "<your-reference-id>",
  "payload": { ... }
}
FieldTypeDescription
typestringEvent type — see below
client_idstringUUID of the client that triggered the event
external_idstring | nullYour own reference ID set on the client (useful for correlation)
payloadobjectEvent-specific data — shape depends on type

Event Types

type: "whatsapp"

Fired for every inbound update from the Meta WhatsApp Cloud API — messages, status changes, etc. The payload is the raw Meta webhook body, forwarded unchanged.

Inbound message example:

{
  "type": "whatsapp",
  "client_id": "f3a1c2d4-...",
  "external_id": "acme_eu",
  "payload": {
    "object": "whatsapp_business_account",
    "entry": [
      {
        "id": "<waba_id>",
        "changes": [
          {
            "field": "messages",
            "value": {
              "messaging_product": "whatsapp",
              "metadata": {
                "phone_number_id": "987654321",
                "display_phone_number": "+1 555..."
              },
              "messages": [
                {
                  "from": "1234567890",
                  "id": "wamid.xxx",
                  "timestamp": "1713600000",
                  "type": "text",
                  "text": { "body": "Hello!" }
                }
              ]
            }
          }
        ]
      }
    ]
  }
}

Message status update example:

{
  "type": "whatsapp",
  "client_id": "f3a1c2d4-...",
  "external_id": "acme_eu",
  "payload": {
    "object": "whatsapp_business_account",
    "entry": [
      {
        "id": "<waba_id>",
        "changes": [
          {
            "field": "messages",
            "value": {
              "messaging_product": "whatsapp",
              "metadata": { "phone_number_id": "987654321" },
              "statuses": [
                {
                  "id": "wamid.xxx",
                  "status": "delivered",
                  "timestamp": "1713600010",
                  "recipient_id": "1234567890"
                }
              ]
            }
          }
        ]
      }
    ]
  }
}

Possible status values: sent, delivered, read, failed


type: "signup_completed"

Fired once after an embed signup OAuth flow finishes — the Meta authorization code has been exchanged for an access token, the phone number registered, and the webhook listener activated.

{
  "type": "signup_completed",
  "client_id": "f3a1c2d4-...",
  "external_id": "acme_eu",
  "payload": {
    "waba_id": "123456789",
    "phone_number_id": "987654321",
    "catalog_id": "456789123",
    "meta_business_id": "111222333",
    "signup_status": "completed"
  }
}
FieldTypeDescription
waba_idstringWhatsApp Business Account ID
phone_number_idstringMeta phone number ID
catalog_idstring | nullMeta Commerce catalog ID (if linked during signup)
meta_business_idstring | nullMeta Business ID associated with the WABA
signup_status"completed"Always "completed" for this event

type: "template_status"

Fired when Meta changes a message template's review status (approved, rejected, paused, disabled…).

{
  "type": "template_status",
  "client_id": "f3a1c2d4-...",
  "external_id": "acme_eu",
  "payload": {
    "template_id": "112233445566",
    "template_name": "order_update",
    "language": "en_US",
    "status": "APPROVED",
    "reason": null
  }
}

status values include APPROVED, REJECTED, PAUSED, DISABLED, PENDING, IN_APPEAL. reason is set when a template is rejected.


type: "flow_completion"

Fired when a user submits (completes) a WhatsApp Flow you sent.

{
  "type": "flow_completion",
  "client_id": "f3a1c2d4-...",
  "external_id": "acme_eu",
  "payload": {
    "message_id": "wamid.xxx",
    "from": "1234567890",
    "token": "<your-flow-token>",
    "response": { "screen_0_name": "Jane", "screen_0_choice": "demo" }
  }
}

token is the flow_token you passed when sending the flow; response contains the fields collected by the flow.


type: "test"

Sent when you click Send test event in the dashboard (or call POST /api/clients/{id}/webhook/test). Use it to verify connectivity and signature validation.


Delivery & Retries

  • Your endpoint must respond with a 2xx status code within 10 seconds.
  • Non-2xx responses and network errors are retried up to 3 attempts with increasing backoff (immediately, +5s, +25s).
  • Every delivery attempt series is recorded. Inspect them per client in the dashboard (Clients → Webhooks) or via GET /api/clients/{id}/webhook/deliveries — status, attempt count, HTTP response code, and last error.
  • Failed deliveries never affect the API response to the original caller.

Security

If a webhook secret is configured (Settings → Webhook), every request includes an HMAC signature header:

X-Signature-256: sha256=<hex digest>

The digest is HMAC-SHA256(secret, raw_request_body). Recompute it on your server and compare with a constant-time comparison before trusting the payload. Also validate the client_id field against your known client IDs.

Reference

Meta — Webhook Payload Examples

On this page