Field note
AI Webhooks: Prevent Duplicate Actions
Separate receiving a webhook from acting on the result.
A background AI job finishes and calls your webhook endpoint. The endpoint downloads the output, writes a report and sends a notification. If the acknowledgement is lost and the provider retries, that apparently simple handler can publish the report twice.
Build two stages: a small receiver that verifies and records the event, and a worker that reconciles the job and performs the permitted next step. The separation makes retries, recovery and investigation much easier.
Verify the original request
Keep the raw request bytes available until verification finishes. Parsing JSON and serialising it again can change the signed content. The Standard Webhooks specification covers signing the body with the message ID and attempt timestamp, together with timestamp checks against replay.
Use the provider's supported verifier rather than writing a new cryptographic scheme. OpenAI's webhook guide shows the SDK unwrap helper and describes duplicate delivery and retry behaviour. Configure the signing secret separately for the endpoint and environment.
Reject failed verification before treating payload fields as trusted routing instructions. Also enforce a reasonable request-size limit and the expected method. A signature is useful evidence of origin; it does not excuse accepting an event type the application has never implemented.
Acknowledge after durable receipt
Insert a record in an inbox table or another durable store, keyed by provider, project or tenant, endpoint and event identity as appropriate. Preserve the event type, related job ID, received time and processing state.
Only acknowledge successful receipt once that record exists. If the process crashes after acknowledging but before saving, the provider may consider the event delivered while your application has lost it.
The worker should claim the inbox record so concurrent deliveries cannot both run the same transition. A duplicate request can receive a successful acknowledgement once the original event is safely recorded. It should not trigger a second copy of the business action.
Keep the transport event ID separate from the business operation ID. Two legitimate events can refer to the same job, and a downstream write still needs protection at its own boundary.
Reconcile the job before using its output
Retrieve the canonical job or response object through the provider's API using the identity stored when you submitted it. Check that it belongs to the expected tenant and task, and inspect its current status.
Do not blindly fetch an arbitrary URL supplied in a payload. Use the provider SDK or a constrained, known endpoint. Associate the result with the submission manifest, not with a customer name inferred from generated text.
Order is another source of mistakes. Stripe's webhook documentation explicitly warns that delivery order is not guaranteed. Although each provider has its own contract, designing a worker that tolerates a late event is a useful defensive pattern. A late “started” notification should not move a locally completed job back to running.
Define allowed state transitions and a policy for contradictory events. Where the provider's current status and an event disagree, record both and reconcile instead of choosing whichever arrived last.
Keep completion separate from acceptance
An AI job marked completed may still contain a refusal, incomplete business data or an output that fails your checks. Validate the expected file type, required fields, record count and source references before making it available to users.
For a report, completion might mean “ready for review”. For a classification batch, it might mean “all submitted IDs reconciled”. The webhook receiver should not decide that a customer message may be sent simply because generation stopped successfully.
The AI finance audit-trail note describes the review record. In an asynchronous implementation, link that record to the job, verified event and accepted output so the chain survives retries.
Test recovery, not only the happy path
Use signed test events to exercise a valid delivery, invalid signature, stale timestamp, duplicate event, unexpected type and an unavailable inbox store. Then crash the worker after each meaningful stage, including after the downstream write but before its local success record.
Inspect the resulting business records. The objective is one intended effect and a recoverable processing state. Verify that a replay of a safely stored event resumes or returns the known result rather than repeating the effect.
Monitor inbox age and permanent failures. Put exhausted retries into a visible review queue with enough identifiers to investigate, while avoiding raw confidential report text in general logs. During rollout, let the worker prepare drafts only. Once verification and recovery behave as designed, enable the specific downstream transitions that the business has approved.
References
- OpenAI, Webhooks. Accessed 17 October 2026.
- Standard Webhooks, Specification. Accessed 17 October 2026.
- Stripe, Webhooks, ordering and duplicate-event guidance. Accessed 17 October 2026.