Webhooks
Five mandate events, written to an outbox in the same transaction as the consumption and delivered every minute, signed over the exact bytes sent.
Events
| Event | Fires when | What to do |
|---|---|---|
mandate.granted | A mandate is issued | Show the person what her agent may now do |
mandate.consumed | A consumption is committed | Reconcile against your own action reference |
mandate.at_limit | A consumption used the last of the period | Warn, or offer a step-up |
mandate.revoked | A mandate is revoked | Stop the agent immediately |
mandate.expired | expires_at passes | Ask for a new mandate before acting again |
Hosted sessions emit no webhooks. There is no session.completed and no session.expired.
Delivery
The event is written to an outbox in the same transaction as the consumption, and mandate-outbox-worker delivers it every minute.
The design answers the obvious question: a webhook sent inside the transaction would hold the ceiling lock hostage to a slow client, and a send before commit would announce something that never happened. Delivery is at-least-once — deduplicate on the event id.
Signature
signature = "sha256=" + HMAC_SHA256(webhook_secret, `${unix_seconds}.${raw_body}`)| Header | Value |
|---|---|
x-sidaxis-signature | sha256=… |
x-sidaxis-timestamp | Unix seconds; reject outside ±300 s |
x-sidaxis-event-id | Deduplicate on this |
x-sieve-signature, x-sieve-timestamp, x-sieve-event-id | The legacy triple, with identical values |
Retries
30 s → 2 m → 10 m → 1 h → 6 h, then parked.
A webhook with no configured secret is not delivered unsigned — it fails explicitly.
Sidaxis is the human authorisation layer. It never moves money, holds funds or settles. The machine-readable contract is openapi.yaml.