Webhooks
Outbound events, signed, and derived from the same record rather than emitted beside it.
Derived, not parallel
Events are produced from the signed record itself. That is the whole design decision: an event stream emitted separately from the audit trail will eventually disagree with it, and then you have two accounts of what happened and no way to choose.
Verifying what you receive
Every delivery carries a GL-Signature header of the form t=<unix seconds>,v1=<hex>. The hex is an HMAC-SHA256 over the ASCII string {t}.{raw body}, keyed by the endpoint's secret. Verify against the raw bytes you received, before parsing them.
The events
gate.decision- A decision was made, whatever its outcome.
intent.denied- A payment was refused.
approval.pending- A payment is waiting for a person.
payment.settled- Value moved.
audit.appended- The record grew.
memory.recorded · memory.consolidated · memory.erased · memory.tainted- Memory lifecycle, where a deployment runs a store.
- Check the signature before parsing the body.
- Treat delivery as at-least-once and make your handler idempotent on the event id.
- Do not order your own state off arrival order. The record is the sequence.
Subscribing
Endpoint management is operator-signed, not an ordinary API call. An agent cannot register somewhere to send copies of your record.
POST /webhooks/endpoints # { url, events[] } -> 201, secret shown once
GET /webhooks/endpoints # list
GET /webhooks/endpoints/{id} # inspect one
PATCH /webhooks/endpoints/{id} # url, events, active
DELETE /webhooks/endpoints/{id} # 204The event surface, described
The whole event surface is projected as a machine-readable description, the same way the request surface is. If you generate your consumer from it, your consumer cannot drift from what we actually send.