Webhooks

Delivery & retries

Curviate delivers every event at least once with automatic retries and transparent endpoint health tracking.

At-least-once delivery

Every event is delivered at least once, so the same event can arrive twice. The top-level id (wdl_<ulid>) is unique per delivery attempt and changes on every retry. Deduplicate on event + data.account_id + data.occurred_at instead.

Retry schedule

A non-2xx response or a timeout schedules a retry with exponential backoff, up to 5 attempts in total:

AttemptDelay after previous failureTotal elapsed from first attempt
1Immediate0 s
230 seconds~30 s
35 minutes~5 m 30 s
430 minutes~36 m
52 hours~2 h 36 m

Each attempt has a 10-second HTTP timeout; no 2xx within it counts as a failure.

Return 200 fast

Verify the signature, return 200, then process asynchronously. Slow work inside the request risks the 10-second timeout and a needless retry.

Endpoint health

GET /v1/webhooks is the only endpoint that returns health:

curl "https://api.curviate.com/v1/webhooks" \
  -H "Authorization: Bearer cvt_live_YOUR_API_KEY"

Each item carries health, last_delivery_at, and delivery_success_rate_7d. GET /v1/webhooks/{id} omits all three.

StatusMeaning
okThe most recent delivery attempt succeeded.
degraded

All 5 attempts for the last delivery failed. The webhook keeps receiving new events. Rarely, it means the webhook's signing secret could not be used: the delivery is recorded as failed without being sent or retried, and no later delivery can succeed. Delete the webhook and create a new one to get a new secret.

degraded is a signal, not a gate. Nothing pauses, and there is no re-enable control. When a webhook is degraded:

  • If it turned degraded right after an event, rather than after the retry window, and your endpoint never received a request, the webhook's signing secret could not be used and no delivery can succeed: delete the webhook and create a new one. Nothing on your endpoint will fix it.
  • Otherwise, fix your endpoint (firewall, TLS certificate, response codes). The next successful delivery returns health to ok.
  • Deliveries that exhausted their 5 attempts are not replayed. Backfill from the API if you need them.
  • To stop deliveries, set enabled to false with PATCH /v1/webhooks/{id} or delete the webhook. Both apply immediately, queued retries included. Re-enabling does not replay what was dropped; new events flow from that moment.

Delivery history

delivery_success_rate_7d is the percentage (0-100) of the last 7 days' attempts that succeeded. Each retry counts as its own attempt, so a delivery that succeeded on its third try adds two failures and one success. It is null, not 100, when there were no attempts. last_delivery_at is the most recent attempt, successful or not: 0 with a timestamp means failing, null and null means never used.

Per-attempt records are not in the API; only the dashboard lists them.

Errors you may hit

Delivery failures never return an API error; they show as degraded health and a falling delivery_success_rate_7d. These come from the management endpoints.

CodeHTTPCauseFix
UNAUTHORIZED401Missing or invalid API key.Send Authorization: Bearer cvt_live_<key>.
RESOURCE_NOT_FOUND404The webhook id is well-formed but not yours.Confirm it with GET /v1/webhooks.
INVALID_REQUEST400A malformed webhook id, or a PATCH body field that is not accepted.Ids are wh_ plus 26 base32 characters.

Full envelope shapes are in the error reference.

Next steps