A webhook is a POST request the API sends to the registered endpoint when a subscribed event occurs. Delivery is asynchronous: the operation that originates the event does not wait for the receiver to respond.

Each endpoint is registered with POST /v1/webhooks, which returns the signing secret. A subscription accepts between 1 and 10 events per endpoint.

Delivery signing

The body of every delivery is signed with HMAC-SHA256 using the endpoint's secret. The signature travels in hexadecimal, prefixed by the algorithm:

X-{Brand}-Signature: sha256=<hex>

Verification consists of computing the HMAC-SHA256 of the body exactly as received, without re-serialising the JSON, and comparing it against the header value. A constant-time comparison avoids leaking information through response timing.

Delivery headers

headercontent
X-{Brand}-Signaturesha256= followed by the HMAC in hexadecimal
X-{Brand}-EventEvent type that originated the delivery, for example validation.completed
X-{Brand}-Delivery-IdDelivery identifier, useful for deduplicating retries
X-{Brand}-TimestampSend time, in ISO 8601 with a Z suffix
Content-Typeapplication/json; charset=utf-8

The Delivery-Id stays stable across retries of the same delivery: it is the value that lets the receiver discard duplicates.

Timeouts and retries

The connection is allowed 5 seconds and the complete request 10 seconds. Past that margin the delivery counts as failed.

A failed delivery is retried up to 5 times, with growing waits:

attemptwait since the previous one
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry6 hours

Not every response is retried. The distinction is whether the failure can resolve itself over time:

receiver responsetreatment
2xxSuccessful delivery
5xx, network error or timeoutRetried
408 and 429Retried: they signal temporary saturation
Any other 4xxNot retried. A 400 or a 404 signals a misconfigured receiver, and repeating the request does not fix it

The receiver's response body is kept truncated to 2,048 bytes for the delivery history, available at GET /v1/webhooks/{id}/deliveries.

Outgoing deliveries are capped at 10 per domain per minute: a receiver with many simultaneous events gets them staggered rather than in a burst.

Automatic deactivation

The endpoint keeps a consecutive-failure counter. A successful delivery resets it to zero.

On reaching 3 consecutive failures, the endpoint moves to auto_disabled state and stops receiving deliveries. The state is available at GET /v1/webhooks alongside the consecutive_failures counter.

Reactivation is manual, via PUT /v1/webhooks/{id} setting the state to active. The auto_disabled value cannot be assigned through the API — only the system sets it.

Delivery states

statemeaning
pendingQueued, not yet attempted
retryingFailed with retries still pending
successThe receiver responded 2xx
failedExhausted its retries or received a non-retryable 4xx

Receiver recommendations

  1. Respond 2xx as soon as the body is received and validated, and process it afterwards. A receiver that processes before responding eats into the 10-second margin and triggers unnecessary retries.
  2. Deduplicate by X-{Brand}-Delivery-Id: a retry repeats the same delivery.
  3. Verify the signature before reading the content.