A reliable webhook receiver verifies the sender, durably records the event, and processes it with duplicate protection. It also recovers when events arrive late, arrive out of order, or never arrive successfully. Treat a webhook as an asynchronous input to a business workflow, not as a function call that happens exactly once.
Define what the event is allowed to change
Start with the business transition. A payment event may update an invoice. A CRM event may refresh a contact. A file-processing event may make a download available.
Write down the provider object identifier, the local record it maps to, the permitted transition, and how to resolve ambiguity. An event name alone is not enough to establish the correct tenant or authorize an arbitrary update.
Separate event receipt from business completion. The HTTP endpoint acknowledges that your system accepted responsibility for an event. A worker may complete the downstream operation later.
For multi-tenant products, maintain a verified mapping between the provider account or object and the owning tenant. Never trust an unsigned tenant parameter merely because it appeared alongside a real provider identifier. See tenant isolation for the broader boundary.
Verify the signature using the original body
Use the provider's official SDK or documented verification procedure. Signature schemes differ, including which fields they cover and how they handle timestamps. Do not create one generic verification function and assume it works for every provider.
Preserve the original request bytes before parsing JSON. Reconstructing JSON can change whitespace or key ordering and invalidate a signature. Both Stripe and Razorpay document raw-body requirements for their verification flows.
Use the endpoint's signing secret, not an unrelated API credential. Keep test and live secrets separate. During rotation, follow the provider's supported overlap behavior and test retries of older events before retiring a secret.
Apply request size limits and validate the expected payload shape after verifying authenticity. A valid signature establishes that the sender generated the payload; it does not make every field suitable for your application's current business rules.
Persist before acknowledging
A practical receipt path is:
- Capture the raw request and required verification headers.
- Verify authenticity and validate the supported event envelope.
- Insert an inbox record with provider, account scope, event identifier, and processing state.
- Arrange durable processing, then return a successful response.
Enforce event uniqueness at the database boundary. Two concurrent deliveries can both pass a “have we seen this?” query, so a query alone cannot prevent duplicate receipt.
If processing is triggered through a queue, close the persistence-to-queue gap. An outbox record in the same transaction as the inbox, or a worker that polls unprocessed inbox rows, can make the handoff recoverable. Returning success after placing an event only in memory can lose it when the process stops.
Return an error when a temporary infrastructure failure prevents durable acceptance. Otherwise the provider may consider the event delivered even though your system cannot recover it. Consult each provider's documented response and retry contract.
Make processing idempotent
Receipt deduplication and business idempotency solve related but different problems. The same event may be delivered twice, and different events may describe the same business outcome.
Suppose two valid notifications both imply that one invoice is paid. The invoice transition should not issue two receipts or grant access twice. Use a stable local operation identity and enforce the business invariant in the transaction that applies the change.
Keep processing states such as received, processing, completed, and failed. A crash while processing must leave a path to reclaim the work. Avoid recording an event as completed before its business effect is committed.
An external effect introduces another crash window. If the worker sends an email or requests a refund and then stops, replay may repeat that call. Use provider idempotency support where available, or a state and reconciliation design that accounts for uncertain outcomes. The background jobs guide explains this distinction.
Expect events to arrive out of order
Stripe explicitly does not guarantee generation order at delivery. Razorpay also warns against depending on webhook ordering. A handler that assumes “created” always arrives before “paid” will eventually encounter an awkward state.
Decide whether the event carries the state you need or merely signals that you should fetch the provider object. For consequential transitions, retrieving current authoritative state can simplify recovery, subject to the provider's API and event model.
Do not use timestamps as a universal ordering solution. Equal timestamps, delayed events, and distinct event types can make that comparison misleading. Prefer provider-supported object versions or explicit business transition rules when available.
For example, an old notification should not reopen an already refunded invoice simply because it was processed last. The handler must understand the allowed transitions and the authority of each input.
Build replay without duplicating effects
Operators need to find an event, inspect its safe diagnostic metadata, and retry processing after fixing the cause. Keep the original provider event ID and relevant object IDs throughout the workflow.
A replay should pass through the same verified business processing rules as normal delivery. It should not bypass duplicate protection or authorize a transition that was invalid the first time. Limit who can trigger replay and record that action.
Retain payloads only as long as the operational need justifies. Events can contain personal information. Restrict access, redact logs, and avoid putting full payloads into every error report.
Monitor the age of unprocessed events, signature failures, repeated processing errors, and provider endpoint status. An empty application error dashboard is not reassuring if the provider disabled delivery upstream.
Reconcile important records independently
A webhook retry policy is not an indefinite recovery service. Add a reconciliation process for important state such as payments, subscriptions, or shipment status.
That process can compare local records with provider state over a controlled window and flag or repair differences. Use checkpoints, rate limits, and audit records. Avoid a background scan that silently overwrites manual corrections without a defined authority model.
Test duplicate deliveries, reversed ordering, malformed payloads, an unavailable database, and a worker crash after a side effect. Confirm that the provider receives the intended response and the final local state remains correct.
For Indian payment integrations, combine these rules with the eligibility and recurring-payment considerations in Stripe versus Razorpay. If you need help integrating a provider with an existing product, Apex's enterprise software team works on those application boundaries.