Webhooks
Signed server-to-server callbacks in both directions, and why an inbound webhook has to break most of the normal request rules.
A webhook is a server calling a server. That sounds ordinary until you notice it breaks the assumptions the rest of the API is built on. There is no logged-in user. The caller is a machine that proves itself with a signature, not a token. And it may need to get through during a maintenance window that keeps every human out. Chronicler handles both directions of this, and each direction has a twist worth understanding.
Inbound: trust the signature, not the session
An inbound webhook, such as a payment provider telling us a charge succeeded, shares one dedicated surface instead of every integration hand-rolling a raw route. That surface carries no user authentication and no maintenance gate, on purpose, because the caller is a server and it has to reach us even while the app is closed to people.
What replaces the login is a signature over the exact bytes of the request body, verified before the payload is trusted at all. This is why the surface takes the raw, unparsed body: re-serialising the JSON first could change a byte and break the signature. A bad signature is a 401 and a transient failure is a 500, both chosen so the provider retries rather than gives up. And the whole surface is hidden from the published API spec by default, surfaced only in development, because it is not for public consumption.
Outbound: let a workspace subscribe to events
Outbound is the mirror image. A workspace registers an HTTP endpoint and receives signed deliveries when chosen events happen. The decision that keeps this clean is where the payload is defined: each context declares which of its own events are public and maps each to a stable, versioned shape, right next to the events themselves, the same way it declares its audit rules. That mapping is the contract.
All the delivery machinery, matching subscriptions, retrying, signing, logging, lives in a hosted-only module, so the self-host edition carries none of it. An endpoint is an event-sourced aggregate, but an individual delivery is just a work-queue row, because a delivery attempt is operational churn, not a fact worth keeping in the event log forever. The feature is gated by an entitlement, so zero registered endpoints means the whole thing is simply off.
Where this connects
The outbound machinery is one of the closed features from Modules and editions, and its event mappings sit beside the audit rules from Security, audit and compliance.
Read the record for the detail
This page is the guide. The full records under docs/adrs carry the signature schemes, the delivery model, and the alternatives that were weighed and rejected.