Prasoon AI
ServicesInsights
Let’s talk
GOOD IDEAS DESERVE GREAT ENGINEERING.Explore the possibilities.

WHAT I BUILD

AI systems

Agents, private knowledge, and production AI.

SaaS platforms

Scalable software, from first release to growth.

HOW WE WORK

Services

Engineering expertise for your next challenge.

Industries

Solutions grounded in your business context.

IDEAS & PERSPECTIVES

Insights

Practical thinking on AI and architecture.

About my approach

Bridging research and production.

Available for select projectsDiscuss your project
All insights/Distributed Systems

Distributed Systems / Practical engineering

Webhook Reconciliation: Building a Ledger That Survives Retries

Design an event inbox, explicit transition rules, and reconciliation jobs so delayed or duplicate payment events cannot silently corrupt product access.

P.
Prasoon ThakurAI systems architect
September 20, 20265 min read
THE ENGINEERING SERIESDistributed Systems
EVENT INBOX / ILLUSTRATIONOne business effect.
evt_204 · first deliveryRecorded
evt_204 · retryDuplicate
ONCEFulfillment ledger
Distributed SystemsIdeas, connected to implementation.
In this article6 sectionsContents +
  1. 01A successful payment is not a completed workflow
  2. 02Give transport identity and business identity separate constraints
  3. 03Commit local effects together
  4. 04Treat state transitions as evidence-dependent decisions
  5. 05Reconciliation is part of the write path
  6. 06Test the crash boundaries before launch

A successful payment is not a completed workflow

A customer pays, the provider sends an event, and your worker grants access. The difficult case begins when the worker commits the entitlement but crashes before acknowledging the message. A retry now looks indistinguishable from unfinished work unless the database records both the business effect and the processing decision.

This article proposes an architecture for that boundary. The event inbox records what arrived; a business ledger records what changed; reconciliation detects what never arrived. Each answers a different operational question. Combining them into a single processed boolean hides the evidence needed to repair an incident.

Stripe documents that webhook deliveries can arrive out of order and that event timestamps are insufficient for ordering or deduplication. Verify signatures against the raw request body, then persist the accepted event before acknowledging it. These are provider-specific requirements, not assumptions to infer from an HTTP success response. Stripe webhook documentation

Give transport identity and business identity separate constraints

Use a unique inbox key containing the provider, account, environment, and event ID. The account and environment prevent test traffic or connected-account identifiers from sharing the wrong namespace. Restrict raw payload access and retention because webhook bodies may contain customer data.

For fulfillment, introduce a different key such as the order ID plus fulfillment type. A second event describing the same paid order must not produce a second shipment, credit, or license. The following is an illustrative schema fragment; provider credentials, authorization, and retention controls belong elsewhere.

System Source
CREATE TABLE webhook_inbox (
  provider text NOT NULL,
  provider_account text NOT NULL,
  environment text NOT NULL,
  event_id text NOT NULL,
  received_at timestamptz NOT NULL DEFAULT now(),
  state text NOT NULL DEFAULT 'pending',
  attempts integer NOT NULL DEFAULT 0,
  PRIMARY KEY (provider, provider_account, environment, event_id)
);

CREATE TABLE fulfillment_effects (
  order_id uuid NOT NULL,
  effect_type text NOT NULL,
  recorded_at timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY (order_id, effect_type)
);

The uniqueness constraint is the concurrency boundary. A preliminary SELECT followed by an unconstrained INSERT does not protect against two workers making the same decision simultaneously.

Commit local effects together

Inside one transaction, lock the relevant order, validate its transition, insert the fulfillment effect, update the entitlement, and mark the inbox event complete. If any step fails, roll back all of them. If the effect already exists, record a duplicate decision without repeating fulfillment.

External side effects cannot join this transaction. An email, shipment request, or remote credit needs an outbox record committed alongside the local effect. Send it later using a stable operation identifier and the destination’s documented retry contract. Never hold an order lock while waiting on an external API.

Stripe's idempotency keys apply to outgoing API requests, with documented retention and parameter-matching behavior. They are not a replacement for a permanent local business ledger. Reconcile uncertain outcomes before reissuing an old operation outside the provider's retention window. Stripe idempotent requests

Treat state transitions as evidence-dependent decisions

Incoming situationSafe decisionEvidence to retain
Same event delivered twiceReuse the recorded resultInbox identity and original decision
Different event for an already fulfilled orderSkip repeated fulfillmentBusiness effect key
Cancellation arrives before creationRetrieve current provider state or deferMissing prerequisite and retry deadline
Refund follows successful paymentApply a separate refund transitionRefund identifier and affected entitlement
Provider request times outReconcile before issuing a new operationStable request key and uncertainty state

Do not impose a simplistic ordering such as “paid always wins.” Refunds, disputes, and reversals are legitimate later business events. Encode permitted transitions and their prerequisites rather than comparing arbitrary status strings or timestamps.

Reconciliation is part of the write path

Periodically compare provider records with local orders using a bounded time window and a durable checkpoint. Include an overlap window so delayed records are revisited. Upsert findings by stable identifiers; a reconciliation pass must be safe to repeat.

Distinguish recoverable lag from contradictions. An unpaid local order with a confirmed remote payment can enter a repair workflow. An order fulfilled twice requires investigation and compensation. Store the repair reason, actor, previous state, and new state, not just the corrected final value.

Assign an owner and maximum acceptable age to unresolved discrepancies. A dashboard full of old “pending” rows is a silent failure even when the endpoint returns only successful responses.

Test the crash boundaries before launch

Inject failure after inbox persistence, after the entitlement update but before transaction commit, and after commit but before acknowledgment. Deliver the same event concurrently. Deliver refund and payment events in reverse order. Disable the worker while keeping the webhook endpoint healthy, then measure recovery after restarting it.

Assert business invariants: at most one fulfillment effect per order and type; no entitlement without a valid paid-state decision; no acknowledged event lost before durable acceptance. Track inbox age, retry count, unresolved reconciliation age, and repaired contradictions. Request throughput alone cannot tell you whether customers received the correct access.

The release decision should depend on these invariants and recovery exercises. A webhook integration is complete when it can explain and repair an interrupted workflow, not merely when a test event returns HTTP 200.

Frequently asked questions

Does deduplicating event IDs make fulfillment exactly once?

No. Event identity prevents repeated delivery of one event from being handled twice, but separate events can describe the same business effect. Fulfillment needs its own durable uniqueness constraint and transactional state transition.

When should a webhook endpoint return success?

After verifying the signature and durably accepting the event into an inbox or durable queue. A successful response must not depend on an uncommitted in-memory task.

About the author

Prasoon Thakur

Prasoon is an AI systems architect focused on reliable agents, retrieval, LLM operations, and scalable SaaS platforms. His work connects model behavior to the controls production teams need: evaluation, observability, security, and cost discipline.

GitHubUpwork profile

Need a reliable production system?

Turn the patterns in this guide into a scoped system design, delivery plan, and measurable reliability target.

Start a strategy session

Related insights

API Engineering

Cursor Pagination Under Concurrent Writes: A Consistency Contract

4 min read
Transaction Design

Inventory Reservations: Preventing Overselling Under Concurrency

4 min read

Active Now • 24/7 Availability

Engaging with teams
from Silicon Valley to Singapore.

I operate as a high-availability resource. To maintain secure collaboration, all global engagements are managed via Upwork.

Discuss your project Direct collaboration through Upwork.

Global / Remote

24/7 Timezone Agnostic

Syncing with USA, Europe, UAE & Singapore

Secure Engagement

Top Rated Expert on Upwork

Prasoon AI

Thoughtful architecture.
Software built for the real world.

Based online. Working worldwide.

Explore

AI systemsSaaS platformsServicesIndustries

Discover

Engineering insightsMy approach

Connect

Upwork GitHub Open to project inquiries

© 2026 Prasoon Thakur

Independent thinking. Dependable engineering.Back to top ↑