Webhooks
Optional. Signed deliveries of ledger rows to your server, with retries. Off until a Manager adds an endpoint.
Polling is the whole integration. Webhooks are for when you would rather be told.
Set one up
A Manager adds an endpoint in the app under Settings, in Webhooks: a URL, and the events to receive, or none selected for all of them. The signing secret, whsec_…, is shown once. Test sends one signed delivery on the spot and shows what your server answered.
Nothing from before the endpoint existed is ever replayed.
A delivery
{
"id": "whd-8Hq2mZ1xPq0a",
"type": "workflow.auto_filed",
"at": "2026-09-17T21:22:48.377Z",
"application": "euw8ydHxTW",
"data": {
"actor": { "kind": "system", "name": "Workflow", "authenticatedAs": "LOS integration (API)" },
"target": { "type": "run", "id": "run-20260917-212242-ef09" },
"detail": { "workflow": "standard-intake v1", "approved": 1, "leftUnassigned": 0, "failed": 0 }
}
}
It is one ledger row. type is the ledger's own action name.
Events worth subscribing to
| Event | Meaning |
|---|---|
application.created | A case was started. |
file.received | A file landed on a case. |
run.ingested | A run's results landed. |
workflow.auto_filed | A workflow finished filing what it recommended. |
document.approved, document.rejected | A filing decision, by a person or a workflow. |
run.failed | A run failed, or the analyzer went silent for 3 minutes, and the case was put back. |
run.cancelled | A person stopped the run and the case was put back. |
The full list is the WebhookEvent enum in the contract.
Verify the signature
Deliveries follow the Standard Webhooks specification. Three headers arrive with each one:
| Header | Value |
|---|---|
webhook-id | The delivery id. The same on every retry, so use it to drop duplicates. |
webhook-timestamp | Seconds since the epoch. |
webhook-signature | v1, followed by the base64 HMAC-SHA256 of id.timestamp.body, keyed with your secret. |
The key is the base64 part of the secret, after whsec_. Any Standard Webhooks library does this for you. Reject a delivery whose signature does not match or whose timestamp is more than a few minutes old.
Retries
Answer 2xx within 15 seconds. Otherwise the delivery is retried 14 times over about 13 hours, from 30 seconds apart up to 2 hours apart, and then marked failed. Answering 410 turns the endpoint off; add it again to resume. The endpoint's row in Settings shows its latest delivery.
Sheaf