POST to your HTTPS endpoint when something you subscribed to changes. Changes are checked every 5 minutes. A webhook receives changes published after it was created; nothing earlier is replayed, and editing a webhook affects future events only.
Admins and members manage webhooks under Connect → Webhooks. Payloads are in Event payloads; checking signatures is in Verifying signatures.
Event types
On the wire,
type is the event and the provider: price.changed.openai, tos.changed.anthropic. A test delivery has the type webhook.test.
Providers
A webhook lists providers by slug (GET /v1/providers), or * (All providers in the app) for every provider, including ones added later. You can also name a provider that is still being added and receive its provider.added.
Credit-priced providers (snowflake) send their price, resource and alias group events only to webhooks that name them, also under *: add the slug beside the wildcard to receive them. Their tos.changed and provider.added reach * webhooks like any other provider’s. See Prices that are not money.
Filters
A provider with no filter sends everything you subscribed to. A filter narrows one provider:- Resources apply to every price event (
price.changed,resource.added,price.revised,price.corrected,resource.removed,resource.renamed) and to the alias group events. An entry matches the provider’s name for the resource (gpt-5) or its canonical id (openai/gpt-5). A rename matches on its old or its new name. - Documents apply to
tos.changed: document slugs such asterms-of-use, fromGET /v1/catalog. provider.addedtakes no filter.
*, a filter may narrow any provider. Changing filters affects future events only.
Alias groups
An alias group is one resource under each provider’s own name for it. A webhook that lists groups hears every price event of their members wherever they are sold, including at a provider that starts selling one later.- Groups narrow: with any listed, each provider the webhook covers sends only the groups’ members, for every price event. A provider’s own resource filter adds to them.
- They do not affect
tos.changedorprovider.added. providersstill decides which providers are covered:*for all, or a list to follow the group at those only.- A group is the same model, not the same offer: each member’s tiers, regions and dimensions arrive as separate events.
- Price events delivered because of a group carry one more field naming it:
"alias": { "id": "8c4e7b1a-2f3d-4e5a-9b6c-0d1e2f3a4b5c", "name": "claude-opus-5.5" }.alias.idis the stable id;alias.nameis the display name, theresourceof the group inGET /v1/comparison/aliases. The rest of the payload is the provider’s own resource and price. A webhook without groups never gets the field. alias.member_addedandalias.member_removedsay when a resource joins or leaves a group. A webhook with no groups (and no resource filter for the provider) receives them for every group.- A group that is deleted later stays on the webhook and matches nothing.
Delivery
The endpoint URL must usehttps on the default port (443) and cannot contain credentials or a fragment. Its host must be a public DNS name, not an IP address or a local name such as localhost, and every address it resolves to must be publicly routable (no private, loopback, link-local, carrier-grade NAT or unique-local addresses). The app refuses to save a webhook with any other URL and shows the reason (its route answers 400 invalid_url); the check runs again at every delivery, which connects only to the addresses it checked. Each delivery is one POST with a JSON body and these headers (the Standard Webhooks scheme):
Your endpoint must:
- Verify the signature over the raw body before trusting anything in it (how).
- Answer
2xxwithin 5 seconds. Any other status, a redirect, or no answer in time is a failed attempt. Redirects are not followed. Queue slow work and answer first. - Deduplicate by
id, after processing. Delivery is at least once: an event can arrive twice, for example when your answer was lost after you processed it. Record anidas processed only once its processing succeeded, ideally in the same transaction as its effects. An event you received but failed to process must not be skipped when it comes again. See Resending an event. - Not depend on order. Events can arrive in any order, for example a resource’s
alias.member_addedbefore or after its first price event under the group. - Ignore what it does not recognize: fields can be added to a payload without changing its
version.
resource.added, resource.removed, resource.renamed, alias.member_added) grows with them. Accept bodies of several megabytes: Express reads only 100 KB by default, so the examples raise its limit.
Timing. Changes are checked every 5 minutes, and a change’s first attempt is made in the check that finds it. A change can only be found once it has landed, and it can land up to 10 minutes after its eligibleAt: it is stamped by the run that read the provider, and the run’s rows land only when it finishes (the reason asOf reads 15 minutes behind). From the moment a change lands, the first attempt usually reaches you within 5 minutes; when there is little to send, at most about 5 minutes plus 200 seconds, because a check stops starting deliveries 200 seconds after it begins and what is left goes out in the next check. Measured from eligibleAt, the worst case is about 18 minutes.
Retries
A failed attempt is retried after 5 minutes, 30 minutes, 2 hours, 8 hours and 24 hours: 6 attempts in all. After the last one the delivery is marked failed. Deliveries go out every 5 minutes, so an attempt can land up to 5 minutes after its scheduled time. Every retry carries the samewebhook-id and a new timestamp and signature.
Deliveries are shared fairly between workspaces: each 5-minute round serves the workspace with the fewest deliveries so far first, so another workspace’s slow or failing endpoints never hold up yours.
Paused webhooks
A webhook whose deliveries keep failing is paused: after 5 attempts in a row have failed, the first of them at least 24 hours before the last. Any successful attempt starts the count over, and so does changing the URL.- The app shows the webhook as paused, with the reason.
- While paused nothing is sent. Deliveries due in that time are marked
failedwith the errornot sent: the webhook is paused, so the delivery log shows what you missed. Send test event and Resend are refused (409 webhook_paused). - Fix the endpoint, then open the webhook’s Settings and choose Resume deliveries. Resume and resend what was not sent also starts over every delivery among the newest 1,000 that was not sent while paused, and says how many. The delivery log keeps a delivery 30 days after its last attempt, so after a longer pause the oldest ones are gone and are not resent.
- Editing a paused webhook keeps it paused.
POST /v1/webhooks/{id}/resume, with ?resend=paused for the second) are not available to API keys.
Delivery log, test and resend
- Deliveries (on each webhook) shows the newest 50 deliveries: status (
pending,succeeded,failed), the next try, and each attempt with its time, HTTP status or error, duration and the start of your response body (up to 1 KB). Expand a row to see the payload. A delivery is deleted 30 days after its last attempt; resending it starts that over. - Send test event (in the webhook’s settings) queues a signed
webhook.testevent through the normal signing and retry path. Tests within the same minute share one event. - Resend starts a delivery’s schedule over, with all 6 attempts, whatever its status: the same event
idand body, a new timestamp and signature. - On a paused webhook, test and resend are refused (
409 webhook_paused): resume it first.
Resending an event
Resend is for an event your endpoint missed or failed to process: a delivery that failed, or one your endpoint acknowledged but then lost. It sends the same event again with the sameid, so a receiver that skips ids it has already processed skips the resend too:
- If the event was never processed (the delivery failed, or you recorded the
idonly after processing succeeded, as Delivery recommends), the resend is processed normally. - To process an event again that you already processed, remove its
idfrom your record of processed events first, then choose Resend. Or handle that one event outside the duplicate check, for example from the payload shown under Deliveries.
webhook.test
Signing secrets and rotation
Each webhook has its own signing secret,whsec_<base64 key>. It is shown when the webhook is created, and admins and members can reveal it again from the webhook’s Settings with Reveal signing secret.
Rotate secret creates a new secret. For 24 hours after a rotation every delivery is signed with both keys: webhook-signature carries two space-separated signatures, and a delivery is valid if either matches. To rotate without dropping deliveries:
- Choose Rotate secret and copy the new secret.
- Deploy it to your receiver within 24 hours. Deliveries keep verifying with the old secret until you do.
- After 24 hours only the new key signs.