Webhooks
Register an HTTPS endpoint with POST /webhooks/endpoints to get notified in real time instead of polling. No endpoint yet? GET /webhooks/events returns your account's own event history directly (same events, same ordering guarantees below).
Delivery guarantees: read before you build a receiver
- At-least-once delivery
- A delivery is retried on a transient failure (5xx, timeout, network error) on a schedule spanning roughly 5 seconds to 6 hours between attempts, for up to ~10 hours total, before it is abandoned. Your receiver must be idempotent: dedupe on the stable
webhook-idheader, which stays IDENTICAL across every retry of one delivery. A genuine re-send viaPOST /webhooks/deliveries/{deliveryId}/replaygets a freshwebhook-id, since it's a deliberate new delivery, not a retry. - NOT ordered
- Two deliveries for the same account can arrive out of order (different endpoints, different retry timing, network jitter). Use the envelope's
sequencefield (monotonically increasing PER ACCOUNT, never per endpoint) as a last-writer-wins guard: discard an envelope whosesequencefor a given resource is lower than one you've already processed. - At-most-once emission
- The underlying OCCURRENCE (not delivery) is recorded once; a retried producer can never cause the same occurrence to be recorded twice. This is orthogonal to delivery being at-least-once: ONE occurrence can still be DELIVERED more than once to the same endpoint (a retry after a timeout whose response you never saw, for instance), and that's exactly what
webhook-id-based dedupe on your end is for. - 3xx and 4xx responses stop retries
- Register the final URL: a
3xxis never followed, so an endpoint that answers301/302(an http → https upgrade, a trailing-slash redirect, a vanity host that forwards) fails every delivery. A3xxor4xxresponse (except429) is NOT retried: treated as "this exact payload/config will never succeed," and the delivery is marked failed permanently.410 Goneis stronger: it disables the ENDPOINT immediately. An endpoint also auto-disables after a sustained run of failed deliveries regardless of status code; re-enable it withPATCH /webhooks/endpoints/{endpointId}.
Verifying a delivery
Every delivery is a POST with a JSON body (the envelope) and three Standard Webhooks headers: webhook-id, webhook-timestamp, webhook-signature. Verify the signature with the secret returned exactly once, at creation (or the last rotation): see POST /webhooks/endpoints and POST /webhooks/endpoints/{endpointId}/rotate-secret. During a secret rotation's 24-hour overlap window, webhook-signature carries TWO v1,<sig> pairs (old and new secret). Validate against either.
Event taxonomy
Pass any of these in eventTypes when registering an endpoint. This table is generated from the same source the delivery code reads: a new event type can't ship here without shipping on this page too.
Hosted pages
| Event | Description |
|---|---|
| page.published | A hosted page finished validating and went live. |
| page.updated | A hosted page's metadata changed (rename, expiry, chat toggle, etc). |
| page.rejected | A hosted page failed validation and was rejected. |
| page.files.committed | One or more files inside a hosted page were added, replaced, or removed. |
| page.moderation.changed | A hosted page's moderation status changed. |
| page.deleted | A hosted page was deleted. |
| page.expired | A hosted page reached its expiry and was taken down. |
Custom domains
| Event | Description |
|---|---|
| domain.verified | A custom domain finished DNS verification and is live. |
| domain.failed | A custom domain failed verification or provisioning. |
Account
| Event | Description |
|---|---|
| account.quota.threshold | An account crossed a usage-quota threshold. |
Platform & safety
| Event | Description |
|---|---|
| platform.abuse.tier_changed | A platform account's graduated abuse-response tier changed. |
| platform.subject.csam_suspended | A single confirmed CSAM detection immediately suspended one of your end-users. |
Deliberately absent: a per-view event. A hosted site can be viewed thousands of times a minute, and turning every view into an outbound HTTP call would put your endpoint's latency and uptime directly on the page-serve path. GET /links/{id}/clicks covers view/click counts on a pull basis instead.
Back to the Platform API reference or the quickstart.

