How to Deduplicate Webhook Deliveries
Process each webhook notification once by using the X-PandaDoc-Webhook-Event-Id header, including when you retry a delivery from the Developer Dashboard.
Problem
A manual retry from the Developer Dashboard sends the same webhook notification again. PandaDoc does not retry automatically when the endpoint is unreachable or returns a non-2xx status. Your handler must process each distinct notification once and ignore duplicates safely.
Prerequisites
- Active webhook subscription (see How to Set Up Webhook Notifications)
- Persistent storage available to your webhook endpoint (database, cache, or equivalent)
- Familiarity with your endpoint's request-header access
Solution
Step 1: Read the delivery identifier
Every webhook HTTP POST includes the X-PandaDoc-Webhook-Event-Id header. Its value is a UUID that identifies that delivery.
POST /webhook-handler HTTP/1.1
Host: your-domain.com
Content-Type: application/json
User-Agent: PandaDoc Webhooks
X-PandaDoc-Webhook-Event-Id: 550e8400-e29b-41d4-a716-446655440000
[{"event":"document_state_changed","data":{...}}]Use this header as your de-duplication key. Do not rely only on document IDs or payload contents: one underlying change can produce multiple webhook event types, each with its own X-PandaDoc-Webhook-Event-Id.
Step 2: Check whether you already processed the ID
Before applying business logic:
- Extract
X-PandaDoc-Webhook-Event-Idfrom the request headers. - Look up the ID in your store of processed deliveries.
- If the ID is already recorded, treat the request as a duplicate: return an HTTP status below 400 and stop (do not re-apply side effects).
Step 3: Process new deliveries and record the ID
If the ID is new:
- Apply your business logic (or enqueue work for asynchronous processing).
- Persist the event ID as processed (ideally in the same transaction or with an equivalent atomic write so a crash between processing and recording does not leave gaps).
- Return an HTTP status below 400 to acknowledge receipt.
Conceptual flow:
event_id ← request header "X-PandaDoc-Webhook-Event-Id"
if event_id is missing:
reject or log; do not process as a normal delivery
if event_id already in processed_ids:
return HTTP 200 # already handled
else:
process payload
store event_id in processed_ids
return HTTP 200Step 4: Keep acknowledgments consistent on duplicates
When you recognize a duplicate, still return success (status below 400). Returning an error status does not make PandaDoc send the webhook again, but it marks that delivery as failed in webhook history.
Verification
- Trigger a webhook event that reaches your endpoint.
- Confirm your logs show a new
X-PandaDoc-Webhook-Event-Idand that processing ran once. - From the Webhooks History tab, retry the same delivery.
- Confirm the retry carries the same
X-PandaDoc-Webhook-Event-Idand that your handler skips re-processing while still returning success. - If you subscribe to related triggers (for example both
document_updatedanddocument_state_changed), confirm those deliveries carry different event IDs and that both are processed.
Troubleshooting
Missing header
- Confirm you are reading HTTP headers case-insensitively (
X-PandaDoc-Webhook-Event-Id). - Confirm a reverse proxy or API gateway is not stripping custom headers.
Duplicates still re-run business logic
- Persist the event ID before acknowledging, or use an atomic "insert if absent" pattern.
- Deduplicate on
X-PandaDoc-Webhook-Event-Id, not only on document ID or event name in the JSON body.
Legitimate events appear to be skipped
- Different webhook event types from the same document change have different IDs; store and compare the header value, not a hash of the whole payload alone.
- Ensure concurrent handlers cannot both pass the "unseen" check; use a unique constraint or equivalent on the stored ID.
Related
- Webhook Events Reference - Header and delivery specifications
- How to Set Up Webhook Notifications - Initial webhook configuration
- How to Verify Webhook Authenticity - Signature verification
- How to Debug and Monitor Webhooks - Retries and delivery history
- Understanding Webhooks - Idempotency and reliability concepts
Updated about 17 hours ago
