Webhooks
What fires a webhook, full and compact payloads, delivery guarantees and signing.
On this page
A webhook sends a signed HTTP POST to a receiver whenever a
document of a chosen type is saved in a chosen workflow status. It is
for integrators who want published content, or the news that
something changed, pushed to them instead of polling for it.
This is what a receiver gets when an article is published:
{
"id": "1748508151402-0",
"type": "document.usable",
"occurredAt": "2026-05-29T08:42:31.402Z",
"tenantId": "421fb813-f96e-4edc-8b88-95d2c76665a4",
"webhookId": "8a3e2c40-3f12-4f6d-902b-6e2f4ad9bb91",
"document": {
"id": "3b2c1a40-f128-4cb6-b8c2-19a4d8d7e2f5",
"type": "core/article",
"version": 3,
"status": "usable",
"title": "Anchored in the harbour",
"snapshotBytes": "<base64 JSON>",
"snapshotSignature": { "alg": "ed25519", "keyId": "void-2026-05", "sig": "<base64>" }
},
"embeddedAssets": [],
"embeddedAudioAssets": [],
"embeddedAudioQuotes": []
}
The event type names the status the document is in, and the signed content of the document travels inside the body.
What fires an event
A webhook subscribes to one document type and to a set of workflow
statuses, or to any status. An event fires each time a document of
that type is saved while it is in a subscribed status. The event type
is document.<status>: document.draft, document.usable,
document.withheld, document.cancelled, or the name of a custom
status from the organisation's workflow.
How often that happens depends on the status:
- Read-only statuses (
usable,withheld,cancelled) do not accept edits, so their events normally mark the move into the status. - Editable statuses (
draftand custom statuses) fire on every save. A webhook subscribed todraftreceives a stream of events while a member writes.
Documents created through the API fire events the same way; see
Pushing documents and
Newswires. Deleting a document fires nothing. The admin
interface can also send a synthetic webhook.test event on request.
Moving a document to usable or withheld produces an immutable,
signed snapshot of it. Events for those two statuses carry the
snapshot, and are emitted only once it is stored; events for every
other status carry the transition only.
Full and compact payloads
Each webhook delivers in one of two payload modes.
Full carries everything a receiver needs to act without a second request:
document.snapshotBytesis the snapshot JSON, base64-encoded. Decoding it gives the whole document as it was at that version.document.snapshotSignatureis an Ed25519 signature over those bytes.embeddedAssets,embeddedAudioAssetsandembeddedAudioQuotesdescribe the images, audio files and audio quotes the snapshot references, with their rights metadata and their own signatures.
For an event without a snapshot, the two snapshot fields are null
and the three arrays are absent.
Compact sends only the envelope: the event id and type, the timestamp, and the document's id, type, version, status and title. It suits a receiver that only needs to know that something changed, such as a cache invalidator or a queue fan-out, or one that reads published content separately through the API.
Webhooks created in the admin interface use full mode. A change of mode applies to new events; a pending retry keeps the body it was first built with. A manual replay rebuilds the body in the mode the webhook has when the replay is sent.
Delivery guarantees
Delivery is at least once. Every event has an id, sent as id in
the body and as the X-Tidenda-Event-Id header, that stays the same
across retries and manual replays. A receiver that records the ids it
has processed and skips repeats handles each event effectively once.
Each event is recorded for every matching webhook before any attempt is made. An internal failure or a restart of the platform delays a delivery but does not lose it, and processing an event again never starts a second series of attempts for the same webhook.
A delivery succeeds when the receiver answers with a 2xx status within 10 seconds. Anything else, including a redirect, which is never followed, is a failed attempt and is retried on a widening schedule: ten retries spread over about 20 hours, after which the event is marked failed and an administrator can replay it.
There is no ordering guarantee. A retry or a replay can arrive after
a newer event for the same document, so a receiver that keeps the
latest state of a document compares document.version before
overwriting what it holds.
Signing
Each delivery carries two kinds of proof. The X-Tidenda-Signature
header is an HMAC-SHA256 over a timestamp and the raw body, keyed
with the webhook's secret: it proves the request came from the
platform, unaltered and recent. The Ed25519 snapshotSignature
proves the document content itself came from the platform, and keeps
proving it after the content is forwarded to systems that never see
the webhook secret. Every receiver verifies the HMAC; the snapshot
signature matters when content is redistributed or archived. See
Signatures and trust.
Limits and failure modes
- A snapshot larger than 2 MiB of JSON is not sent. The event shows as a permanent failure in the deliveries log, as does an event whose snapshot cannot be found. Neither is retried automatically.
- A receiver that answers after 10 seconds is recorded as failed even if it processed the request, so the retry arrives as a duplicate.
- A receiver that answers with a redirect fails every attempt. Register the final URL.
- Events that occur while a webhook is disabled are not delivered later, and a retry that falls due while the webhook is disabled or revoked is dropped.
- Payloads change only additively. New fields, event types and headers may appear; an incompatible change ships under a new event type prefix.