The event envelope

Every delivery has the same outer shape and the same headers. Only data varies by event type.

Every delivery, of every type, has the same outer shape. Only data varies.

{
  "id": "3a6f19c8-2b74-4de0-91a5-6c8f13d0e4b7",
  "type": "transfer.completed",
  "apiVersion": "1.0",
  "createdAt": "2026-08-21T10:14:22.183Z",
  "environment": "test",
  "tenant": "acme",
  "subject": { "type": "financial_transaction", "reference": "3f1c6a2e-8b47-4d19-9a0e-15d2c7b8e441" },
  "owner": { "type": "ORGANIZATION", "reference": "9b2e17a4-5c33-4f80-8de1-6a4f92c0b7d5" },
  "data": {
    "reference": "3f1c6a2e-8b47-4d19-9a0e-15d2c7b8e441",
    "type": "PEER_TRANSFER",
    "status": "COMPLETED",
    "amount": { "amount": "5000.00", "currency": "NGN" },
    "fee": { "amount": "26.88", "currency": "NGN" },
    "narration": "October rent",
    "postedAt": "2026-08-21T10:14:22.101"
  }
}

Fields

Field Type Notes
id string The event’s reference. Stable across every endpoint and every retry. Your idempotency key.
type string The catalogue name. Route on this.
apiVersion string The payload contract version for this event type, frozen once published.
createdAt string When the event happened, ISO-8601 UTC. Not when this attempt was sent.
environment string test or live: the environment the event happened in.
tenant string Your tenant.
subject object { type, reference }: what the event is about, and what you’d fetch to see more.
owner object { type, reference }: whose event it is (USER, ORGANIZATION or TENANT).
data object The event-type-specific payload.

Three notes that save an afternoon each

  • data reuses the shape the REST API already returns wherever one exists, so most fields will look familiar.
  • Fields are added, never removed or renamed, within an apiVersion. Parse permissively.
  • Event type names never change. A superseded type is marked DEPRECATED with a retirement date and keeps firing until then.

Headers on every delivery

Header Example Notes
User-Agent Bridge-Webhooks/1.0
Content-Type application/json
Bridge-Event-Id 3a6f19c8-… Same as id. Dedupe without parsing.
Bridge-Event-Type transfer.completed Route without parsing.
Bridge-Api-Version 1.0
Bridge-Environment test The environment the event happened in.
Bridge-Timestamp 1787307262 A copy of the t= in the signature, for convenience. Verify against the t= inside Bridge-Signature.
Bridge-Signature t=1787307262,v1=6f3c… See Verify a signature.
your own headers X-Merchant-Route: ledger-sync Whatever you registered on the endpoint.

The body is byte-identical across retries, so the signature doesn’t change and deduping on a hash of the body still works.

No Authorization header is ever sent, and an endpoint can’t add one. A bearer token in a header has none of the signature’s properties: it isn’t bound to the body, doesn’t expire and can’t be rotated with overlap. Verify the signature.