Request headers and verification
Compute HMAC-SHA-256 using the full whsec_ secret over the timestamp, a literal dot, and the exact raw UTF-8 body. Compare hex signatures in constant time. Reject invalid timestamps or timestamps more than 300 seconds away from the receiver clock. During rotation, accept a match against either active secret and any offered signature.
Persist the raw notification or enqueue it durably before answering 2xx. Deduplicate work by Linktrail-Delivery-Id in durable storage. Do not mark a delivery processed before its work has been durably accepted. The event ID supplies a separate identity when reconciling with pulled feed records.
Per-event payload
feed is events or target_events. subject_id identifies the watch or destination. transition is the resulting state; previous is the prior state. State, uncertainty, checked time, observation identifier and evidence reference may be null when absent. Evidence references contain no publisher page bytes. Read evidence with your own authenticated credential; expired bytes return EVIDENCE_EXPIRED.
resume.feed identifies the independent feed. resume.after_sequence gives the sequence position for recovery through the named endpoint. Maintain your own durable checkpoint from the authenticated feed so a delivered notification cannot cause you to skip earlier unseen events.
Event types
Confirmed absence/unavailability requires repeated conclusive evidence according to the relevant reducer; a blocked or incomplete check remains uncertain. Endpoint event-type filters advance their cursor across filtered-out types. A syntactically accepted type with no emitter matches no events.
Digest payload
Digest mode summarizes the events feed over daily or weekly windows. A payload contains:
Each subjects row has subject_id, project_id, previous, state, uncertain, events, types, first_seen_at, last_seen_at, checked_at and evidence_key. A subject that changes repeatedly appears once with its final state and its event count. The read limit and subject limit are separate; inspect both flags. Quiet windows are delivered with computed:true and zero totals. Digest notifications use the same signature, delivery ID and retry mechanism.
Retention gap payload
Dispatch onkind before treating a message as a normal event. When retention overtakes an endpoint cursor, a kind:"history_gap" notification has type webhook.history_gap and code CURSOR_EXPIRED. It includes id, feed, workspace_id, project_id, created_at, computed:false, totals:null and resync_required:true.
history_gap contains after_sequence, through_sequence, scope:“workspace_feed”, reason:“history_unavailable” and affected_project_events:“unknown”. snapshot_endpoint names the current-state read; resume_cursor is the supplied opaque resume token. resume carries feed, after_sequence and endpoint. A digest gap also carries delivery_mode:“digest” and window. Preserve the gap explicitly. A current snapshot cannot recover expired history.
The local receive command currently accepts per-event identity only. Handle digest and history-gap notifications in your receiver and use the authenticated feed/snapshot endpoints for recovery.
Retries and recovery
A 2xx response completes a delivery. HTTP 410 disables the endpoint immediately with receiver_reported_gone. Other failures retry with a ten-second send timeout and exponential backoffmin(3600, 15 * 2^min(attempt,8)) seconds. Actual delivery ticks can make retries later than that minimum. Twenty consecutive failures disable the endpoint; a successful delivery resets the count. Oversized payloads above 256 KiB fail terminally.
Read list_webhook_deliveries to inspect failures. Setting an endpoint active resumes pending work, but does not resend terminal failed rows. Recover missing notifications through list_link_events and list_target_events, using separate cursors. Terminal delivery rows are retained for 30 days after becoming terminal; pending rows are not pruned by that terminal-history policy.
Rotation and endpoint changes
Call rotate_webhook_secret with retire:false, save the once-returned secret, and deploy receiver support for both secrets. After verifying new-secret delivery, call it with retire:true to retire the older secret. A second rotation is refused while a rotation is open. An endpoint URL is not an editable field. Create and verify a replacement endpoint, then disable or delete the old endpoint. Deduplicate notifications while both operate. Review HTTP route differences if using resource aliases.This reference describes the development delivery contract. Check hosted availability before depending on delivery.