Skip to main content

What counts as success

Any 2xx. Custral reads the status code and nothing else: the response body is ignored, and a redirect is not followed. Anything else, including a redirect, a timeout or a connection failure, is a failed attempt and is retried.
Acknowledge first, work second. The 10 second timeout covers your whole response, so push slow work onto your own queue and return 200 immediately. A handler that calls three other APIs before responding will eventually cross the line, and Custral will retry an event you already processed.

Redirects are not followed

A delivery is a signed POST. Following a redirect would send that signed body to whatever address the Location header names, so Custral stops at the 3xx, records it, and retries the URL you registered. It never requests the Location. The common causes are an http:// URL on a server that forces https://, a trailing slash your framework adds or strips, and an apex domain that redirects to www. Register the URL your endpoint redirects to.

The retry schedule

Custral makes up to 8 attempts over about 24 hours: the first as soon as the event fires, then 7 retries with a longer wait before each one. The short waits cover a dropped connection or a process restart. The long ones cover a deploy or an outage that runs for hours. Each wait starts when the previous attempt fails, so an attempt that hits the 10 second timeout adds those 10 seconds to the times above. After an outage, a delivery can arrive hours after your endpoint is back online.
If all 8 attempts fail, the delivery is not tried again and there is no way to replay it by hand. That takes an outage, or a handler that keeps returning errors, lasting longer than about a day. If you cannot afford to miss an event, reconcile from the API after a long outage.
Two failures skip the retries entirely, because retrying cannot help:
  • The endpoint’s URL, or an address its hostname resolves to, does not pass the safety check.
  • The subscription was deleted or set inactive after the event was queued.

Your handler must be idempotent

Assume every event can arrive more than once. A retry after a timeout is the common case: your server did the work, the response was slow, Custral never saw it, and the same delivery lands again. Deduplicate on the envelope’s id, which is stable across every attempt of one delivery:
createdAt is not stable across retries. It is stamped per attempt, so it is useful for measuring delivery latency and useless as a deduplication key.

Order is not guaranteed

Deliveries are processed concurrently, so two events about the same record can arrive out of order. Do not infer sequence from arrival. If order matters, treat the payload as a notification that something changed and read the current state back:

Fan-out

One event delivers once per matching subscription. Three endpoints subscribed to record.created produce three deliveries with three separate ids, each retried independently. An endpoint that is down does not hold up the others.

The delivery log

Every attempt is recorded against the subscription before anything is sent, so a delivery survives a worker restart. Open an endpoint under Settings → Developers → Webhooks to read the last 100, newest first. A failed row is not final while attempts is below 8. Another attempt is still coming, unless error is webhook_inactive_or_missing or starts with unsafe_url:, which are never retried.
The log holds one row per delivery, not per attempt. responseStatus and error describe the most recent try, so you can see that a delivery failed three times and what the last failure was, but not the individual history of each attempt.

Error codes

error holds a code, never an error message. A failed connection’s message can include a network address, and everyone in the workspace can read the delivery log. Transport failures carry the underlying system code after a colon when there is one.

Limits worth knowing

What’s next

Troubleshooting

Nothing arriving, or arriving and failing.

Managing subscriptions

Create, rotate, and delete endpoints.