Webhooks

Delivery and verification

How Outqube sends, signs and retries webhook events, and how to verify them.

Outqube sends these messages to your endpoint (add one with createWebhook). Each is an HTTP POST with a JSON body, sent from Outqube's servers. They are listed in the webhooks map of this document because you implement the receiving side.

Subscribing. An endpoint gets the event types in its events list, or all of them with ["*"]. The ping test message is never part of a subscription: it is sent only when you call testWebhook. An app can have 10 endpoints at most. The URL must be https, on a public host and a standard port.

Headers. Every message carries webhook-id (the message id: the same on every retry, so use it to drop duplicates), webhook-timestamp (Unix seconds when this attempt was signed), webhook-signature, content-type: application/json and user-agent: Outqube-Webhooks/1.

Verifying. Messages are signed as Standard Webhooks defines: an HMAC-SHA256 over {webhook-id}.{webhook-timestamp}.{raw body}, keyed with the endpoint's secret (whsec_ followed by the base64 of 32 random bytes; decode the base64 part to get the key) and sent base64-encoded as v1,<signature>. Verify the raw request body, before parsing it, with any Standard Webhooks library, and reject a timestamp far from the current time. For 24 hours after a secret rotation the header holds two signatures, one per secret, separated by a space: accept the message if either matches.

Answering. Reply with any 2xx status within 10 seconds. Anything else is a failed attempt: another status, no answer in time, a connection error, or a redirect (redirects are not followed).

Retrying. A failed message is sent again up to 8 times, after 5 s, 30 s, 2 min, 10 min, 30 min, 1 h, 3 h, 6 h, each wait varying by up to 20% either way: 9 attempts over about 10.7 hours. The ping test message is sent once and not retried.

Disabling. A message that runs out of retries counts against the endpoint, and any delivery resets the count. After 5 such messages in a row Outqube switches the endpoint off (enabled false, disabled_reason failures). Turn it on again with updateWebhook.

Order and duplicates. Delivery is at least once and not ordered: a retried older event can arrive after a newer one, and the same event can arrive twice. Drop duplicates by webhook-id, use the payload's timestamp and status to order events, and fetch the run from api_url when order matters.