What counts as a successful delivery
A delivery succeeds when your endpoint responds with any2xx status code within 15 seconds. Anything else is a failed attempt: a 4xx or 5xx, a timeout, a connection error, and 3xx redirects, which are not followed.
Respond before you process. Do the minimum to durably accept the event (verify the signature, push it onto a queue), return 200, and do the real work asynchronously. Handlers that do heavy work inline risk hitting the timeout and receiving duplicate deliveries.
Retries
A failed delivery is retried automatically: up to 50 times over roughly 11.5 hours, with the delays growing quickly at first and then settling into a steady cadence:
Every retry of an event carries the same
event_id in the body and the same svix-id header, so a retry that arrives after a slow-but-successful first attempt is easy to recognise and skip.
If all retries fail, that delivery is marked failed in the endpoint’s delivery log. Nothing is lost: you can resend it or recover a whole time range once your endpoint is healthy again.
Automatic disabling and re-enabling
An endpoint that fails continuously for about 5 days is disabled automatically, and no further events are sent to it. This protects both sides: a dead endpoint doesn’t accumulate an endless backlog. Disabling isn’t the end, though. For the next 12 hours, CloudTalk keeps probing the endpoint with a test event, backing off from a few minutes to a couple of hours between probes. The moment a probe gets a2xx, the endpoint is re-enabled automatically and the deliveries that failed while it was down are replayed. If it hasn’t recovered within those 12 hours, it stays disabled until you re-enable it yourself.
Account admins receive an email when an endpoint is automatically disabled and again when it is re-enabled.
To re-enable an endpoint manually: fix the endpoint, open it under Account → Webhooks, switch it back on, then use Recover to replay the deliveries that failed while it was disabled.
Delivery log, test events, and replay
Every endpoint under Account → Webhooks has its own operations panel:- Delivery log: every delivery attempt with its event type, status, response code, and time.
- Resend: redeliver any single event from the log, for example after fixing a bug in your handler.
- Recover: replay every failed delivery since a time you choose, in one action. Use it after an outage on your side.
- Send test event: pick an event type and CloudTalk sends a sample payload to your endpoint, so you can verify connectivity and signature handling before real traffic arrives.
Data retention
Event payloads are stored for 30 days. Within that window you can open a delivery in the log, inspect the payload CloudTalk sent, resend it, or recover a range of failed deliveries. After 30 days the payload is deleted and can no longer be viewed or replayed. Plan around it: your own system should be the system of record for anything you need long term. Thirty days is comfortably longer than the retry and auto-disable windows above, so a normal outage never risks losing replayable events.Duplicates and idempotency
Delivery is at-least-once: in rare situations (a retry racing a slow response, infrastructure failover) the same event is delivered more than once. Duplicates always carry the sameevent_id.
Make your handler idempotent: claim the event_id before you do the work, not after. Insert it into a store with a uniqueness constraint (a unique index, SET NX in Redis, or an equivalent atomic operation) and only proceed when the insert succeeds. Checking first and recording afterwards leaves a window in which a retry and the original delivery, or two workers, both pass the check and both repeat the side effect.
Keep those records for at least as long as you might replay. Resend and Recover reach back over the full 30-day retention window, so a store that expires after an hour will happily reprocess a three-week-old delivery and repeat whatever side effect it had. Thirty days of event_ids is the safe floor; a durable idempotency key on the work itself (the order you create, the ticket you open) is better still.
Ordering
Events are not guaranteed to arrive in order: acall.answered can occasionally arrive before its call.started, and events for different resources interleave freely.
- Sort by
occurred_atwhen sequence matters. - Don’t treat a “later” lifecycle event as an error when the earlier one hasn’t arrived yet.
- When you need the current state of a resource rather than its history, fetch it from the REST API instead of reconstructing it from events.
Best practices
Verify every delivery
Verify every delivery
Reject requests that fail signature verification with a
401. Never process an unverified payload.Acknowledge fast, process async
Acknowledge fast, process async
Return
2xx as soon as the event is durably accepted: queue it and process in the background. This keeps you inside the timeout and lets you absorb bursts.De-duplicate on event_id
De-duplicate on event_id
Track processed
event_ids and skip repeats. De-duplication plus idempotent processing makes at-least-once delivery indistinguishable from exactly-once.Tolerate the unknown
Tolerate the unknown
New event types and new optional fields appear over time without a version bump. Ignore event types you don’t handle and fields you don’t recognize instead of failing.
Subscribe only to what you use
Subscribe only to what you use
Fewer subscribed events means less traffic to secure, queue, and pay attention to, especially high-frequency events like
user.status_changed.Exempt the route from CSRF protection
Exempt the route from CSRF protection
Web frameworks often apply CSRF checks to all POST routes. Your webhook route authenticates via signatures, not sessions: exempt it, or every delivery will be rejected.
Use HTTPS with a valid certificate
Use HTTPS with a valid certificate
Endpoints must be HTTPS. Expired or self-signed certificates cause failed deliveries that count toward automatic disabling.
Payload conventions worth knowing
- Empty means absent. Optional fields with no value are omitted, never
null. - Timestamps are RFC 3339 UTC (
2026-08-17T09:34:21.512Z); sub-second precision may vary. - Versioning is per event type.
versionbumps only on breaking changes; both versions may be delivered during a migration window while you switch over. - Recordings and transcripts arrive by reference, never inline: fetch them through the API with your own credentials.