Webhooks
Webhooks overview
Signed webhooks that push each ledger entry to your endpoint.
A workspace can register HTTPS endpoints that receive its ledger, pushed after each entry is appended, instead of polling the ledger API. Every request is signed, so a receiver can prove it came from Vestiarion without trusting the network.
Adding an endpoint
An owner or an admin adds an endpoint on the workspace's Settings page, /o/<slug>/settings, under Webhooks. Other members see the list of endpoints but not the controls, and see only each endpoint's host: Who sees what.
The URL is checked on the spot against the URL rules. When it passes, Vestiarion shows the endpoint's signing secret: whsec_ followed by 32 random bytes, base64url-encoded. It is shown once. It is stored encrypted, bound to the workspace and the endpoint, and cannot be shown again. If you lose it, remove the endpoint and add it back, which issues a fresh secret.
A workspace holds at most 5 active endpoints.
The same panel can send a test event to an endpoint, and remove an endpoint. There is no re-enable: a disabled endpoint is removed and added again, which also issues it a new secret.
Events
| Event | When |
|---|---|
ledger.appended | A new entry was appended to the workspace's ledger. The body carries the entry. |
webhook.test | Someone sent a test event from Settings. The body carries no entry. |
Payload and headers shows both.
Timing
Every delivery is queued in the same transaction that appends its ledger entry, and goes out from one of three places:
- Right after the request that appended the entry. Approving a payment, pausing the agent, changing a member or a key: once the response has been sent, a short dispatch (at most 20 seconds) sends what is due. Entries appended within a few seconds of each other can share one dispatch. The person's request never waits on your endpoint.
- Right after each scheduled agent tick's cycles, for the entries they appended. The dispatch gets what is left of the tick's own time budget: at most 30 seconds, and none at all when nothing is left; the queue then waits for the next dispatch of any kind.
- On a schedule, every 10 minutes, for retries and anything still queued. The schedule runs on GitHub Actions, which can start a scheduled run late or skip it under load, so a retry can wait longer than its backoff step; the next append or tick also picks it up.
A dispatch run is bounded by 60 seconds and 500 deliveries, claimed in batches of 25. No batch is claimed, and no request is started, with less than about 12 seconds left (one request's 10-second timeout plus a margin), so nothing is left half-sent when a run ends; a queue larger than one run is finished by the next.
A test event is sent differently. Sending it from Settings delivers it at once, inside that person's own request: a single attempt, at most about 10 seconds, with no retry whatever the result.
In this section
- Payload and headers: the body and headers of every delivery.
- Verifying signatures: check the delivery's HMAC, and the ledger entry's own Ed25519 signature.
- Retries and disabling: the retry schedule, and when an endpoint is disabled.
- Security: the URL rules, how long delivery records are kept, and who sees what.
- Delivery guarantees: at least once, not in order, and how a receiver handles both.