Why this exists
Polling for state changes works until it doesn’t. You either poll too often and waste both sides’ budget, or you poll too rarely and act on stale data. Webhooks invert the relationship: gavAI knows the moment something changes, so gavAI is the right party to initiate the notification. Your endpoint stays quiet until it has work to do. The trade-off is that you now run a public HTTPS endpoint that an external service writes to, which means you need to (a) confirm each call really came from gavAI and (b) handle the fact that the network is not reliable and the same event may arrive more than once. Verification and idempotency, the next two pages in this section, cover those two responsibilities.The delivery flow
The three identifiers worth keeping straight:What every delivery carries
The JSON body always includes anid field. That id is stable across retries of the same logical event and is the value you should dedupe on. The delivery id in the header changes on every retry — useful for log correlation, useless for dedupe.
The event envelope
Every delivery body is wrapped in the same envelope shape. The keys are stable across event families, so a single deserializer can handle every event type your handler receives. The envelope is also what the signature covers. The HMAC input is computed over the exact bytes of the entire envelope body, not overdata alone — verify the signature before you reach for any field inside.
Delivery semantics, named
How a subscription is born
Register a webhook in/console/settings/webhooks (or via the API). gavAI returns a signing secret of the form whsec_<64 hex chars> exactly once. Store it in your secret manager before closing the dialog — there is no rotation endpoint, and there is no way to retrieve it later. If you lose the secret, delete the subscription and create a new one.
Each subscription belongs to one workspace and one URL. Use a separate subscription per environment — production traffic landing on a staging handler is a bad day.