Guide
Handle signed Dome webhooks without repeating the business action
Suppose a denied tool call should open one review ticket. The webhook arrives, your receiver opens the ticket, and later someone replays the delivery. Without a duplicate check, the same event can open a second ticket.
For Dome integrations, distinguish the event from an individual delivery. Verify the signed request, durably accept it, and deduplicate the business action using a stable event identity. The procedure below is an integration design, not a claim that Dome runs your receiver's transaction.
First, subscribe to an event Dome delivers
Start with the webhook documentation. Choose a supported operation and supported filters for the workspace subscription.
For example, a denied-operation workflow uses the operation name and a result=denied filter where supported. Do not invent an access.denied event name. Do not assume every audit event is a webhook event: model.call is available in audit but is not delivered through webhooks.
Write down what the receiver will do when the selected event arrives. “Create a review task for a denied tool call” is testable. “Send all security information” is not a useful subscription contract.
Track the event separately from its delivery
A delivery identifier answers, “Which HTTP delivery am I diagnosing?” An event identifier answers, “Have I already handled this underlying event?”
Dome's replay behavior matters here: replay uses a new webhook-id while preserving Dome-Event-Id. Deduplicating only on webhook-id therefore does not prevent replay from repeating the business action.
Use a durable key based on the stable event ID and the receiver's intended processing namespace. If the same event legitimately drives both a ticket and an archive, those are two consumers, not an accidental duplicate. Give each intended action its own consumer key.
Keep the delivery ID for diagnostics alongside the event ID. Neither identifier is a substitute for signature verification.
Accept the event without losing or repeating work
- Capture the raw request body and the required signature headers without rewriting the body.
- Verify the Standard Webhooks signature and timestamp using the subscription's signing secret and a suitable verifier. Reject unverified requests before acting on their content.
- Parse the verified envelope. Check that it contains the expected event identity and belongs to a subscription your receiver handles.
- In durable storage, insert a receipt keyed by consumer namespace and
Dome-Event-Id. Treat an existing receipt as a duplicate according to its processing state. - Atomically persist the work to be done with that receipt, for example through a transaction and outbox. A receipt recorded before an unprotected queue write can otherwise lose work after a crash.
- Return a success response within Dome's documented ten-second receiver window once acceptance is durable. Perform slow downstream work asynchronously.
- Make the downstream action idempotent too, or persist its result so a worker retry can recover without creating another ticket or notification.
This is a recommended receiver architecture. Its guarantees depend on your storage, queue, and downstream API. A database uniqueness constraint alone does not make a non-idempotent external action exactly-once.
Test retries, replay, and crashes
| Fixture | Expected receiver behavior |
|---|---|
| Valid new event | One durable receipt and one intended action |
| Body changed after signing | Rejected before a business action |
| Invalid or unacceptable signature timestamp | Rejected according to verifier policy |
| Duplicate delivery of the same event | Existing receipt recognized; no extra action |
| Replay with a new delivery ID and the same event ID | Same consumer action not repeated |
| Crash after durable acceptance, before processing | Pending work resumes |
| Crash after downstream success, before local completion | Idempotency key or reconciliation prevents duplication |
| Durable storage unavailable | Do not acknowledge acceptance that was not recorded |
Keep synthetic payloads in the test suite. Avoid storing full production bodies in application logs merely to debug delivery.
Separate delivery health from incident judgment
A signed denial event tells the receiver what was refused. It does not tell an operator whether the refusal was expected, caused by a rollout defect, or worth investigating as suspicious activity. Keep those incident-response decisions separate from the receiver's job of delivering one review ticket.
Keep a short receiver runbook: the selected events, signature verifier, duplicate key, durable receipt, retry behavior, and recovery owner. Test the replay case before relying on the integration. One event should still produce one intended ticket, even after a failed delivery or worker retry.