Skip to main content
Webhooks let your code react to events from your WhatsApp numbers in real time. Every delivery is signed, retried, and recorded so you can debug from the dashboard.
The Webhooks dashboard lives at wassist.app/developers/webhooks. Create one there, copy the signing secret, and you’re done.

How a delivery works

Behind the scenes:
  1. The event is committed to our outbox as a WebhookDelivery with a stable UUID.
  2. A worker POSTs the JSON body to your URL.
  3. We record an attempt row with status code, duration, and response body.
  4. On 5xx or network errors we retry up to 3 times with exponential backoff (10s, 30s, 90s).
  5. After 20 consecutive failures the webhook is auto-disabled and you get a banner in the dashboard.

Headers

Verifying signatures

Compute HMAC-SHA256 over the string <timestamp>.<raw body> using the webhook’s signing secret and compare it in constant time to the v1 component.

Event catalog

message.received

Fired when a customer sends a message to one of your numbers.

Ad attribution (Click-to-WhatsApp)

When the customer starts the chat by tapping a Click-to-WhatsApp ad (or a boosted post), message.referral carries the ad details WhatsApp attached to that message. latestReferral has the same shape and holds the most recent referral in the conversation. That means later messages in an ad-driven chat can still be attributed to the ad.
ctwaClid is the Click-to-WhatsApp click ID. Send it back to Meta via the Conversions API for Business Messaging to attribute leads and purchases to the ad. sourceId is the Meta ad (or post) ID. You can also look these up later: they’re on referral for each message returned by GET /conversations/{id}/messages/, and on latestReferral for each conversation. To filter conversations, use hasReferral, referralSourceId or ctwaClid. See Ad Attribution for the full field reference and a Conversions API example.

test.ping

Triggered by the dashboard Send test event button. Same envelope as a real event with a stub payload — use it to validate signature verification in CI.

Best practices

  • Idempotency. Store the X-Wassist-Delivery ID and reject duplicates. We use the same ID across all retries.
  • Respond fast. Send a 2xx within 10 seconds and do real work asynchronously. We treat slow responses as failures and retry.
  • Replay protection. Reject events where X-Wassist-Timestamp is more than 5 minutes from now.
  • Rotate secrets. From the dashboard, Rotate secret generates a new secret. The old one stops working immediately.
  • Local development. Use the CLI: wassist listen tunnels real events to localhost without exposing a public URL.

Replaying deliveries

Every delivery is stored with its full payload. From the dashboard you can:
  • Filter deliveries by status (all / failed / succeeded).
  • Open a delivery to see request headers, response body, and the timeline of every retry attempt.
  • Click Replay to send a fresh copy of the same payload (it gets a new X-Wassist-Delivery ID).
You can also replay programmatically via POST /api/v1/webhook-deliveries/{id}/replay/.