Skip to main content
When a trigger fires and a matching subscription exists, Exo sends an HTTP POST request to the subscription’s URL. This page describes the request format.

Request headers

Request body

string
The trigger that fired: on_create, on_update, or on_delete.
string
The resource name (e.g. order, contact).
string
Unique event UUID created by Exo for this trigger occurrence.
string
Unique delivery UUID for this specific webhook attempt.
object
The transformed record data, as returned by the resource’s transform method. Date fields are formatted as ISO-8601 strings.
string
UTC timestamp of when the event was processed, in microsecond precision: 2026-03-28T14:30:00.123456Z.

Example payload

Signature verification

The X-Exo-Signature header contains an HMAC-SHA256 hash of the raw JSON body, computed using the subscription’s secret as the key. To verify:
  1. Read the raw request body (before parsing)
  2. Compute sha256= + hex-encoded HMAC-SHA256 of the body using your subscription’s secret
  3. Compare with the X-Exo-Signature header using a constant-time comparison
See Verifying signatures for code examples in PHP, Node.js, and Python.

Delivery behavior

  • Webhooks are delivered via Laravel’s queue system as background jobs
  • Each delivery timeout is controlled by exo.webhook.timeout (30 seconds by default)
  • If your endpoint returns a non-2xx status or times out, Exo retries based on max_attempts and backoff configuration (or per-Resource webhook overrides)
  • Admins receive webhooks for all records; regular users only receive webhooks for records they own
  • Inactive subscriptions (is_active = false) are skipped during delivery