Reconciliation: The Boring Infrastructure That Prevents Financial Disaster
How we ensure every dollar collected matches a booking, and every booking matches a payment — automated reconciliation, mismatch detection, and audit trails.

$14,000 collected. $13,800 in confirmed bookings. Where's the $200?
That's the kind of question that makes finance teams nervous and engineers queasy. The answer could be a refund that hasn't been processed, a webhook that was lost, a booking that was confirmed by the travel provider but not recorded in our database, or a dozen other edge cases that arise when money flows through multiple systems.
Reconciliation is the infrastructure that catches these discrepancies. It's not glamorous. Nobody writes conference talks about it. But it's the difference between a financial system you can trust and one that slowly drifts out of alignment until someone notices the hard way.
The matching algorithm

Our reconciliation runs as a scheduled job. It compares two sets of records: payments (charges captured through our payment processor) and bookings (confirmed reservations in our database).
For each payment, there should be exactly one matching booking. For each confirmed booking, there should be exactly one matching payment. The matching criteria are: booking ID (linked to the payment intent), amount (the payment amount should match the booking fare), and timing (the payment should occur within a reasonable window of the booking confirmation).
The matching algorithm classifies each record into one of three categories:
Matched. Payment and booking found, amounts match. Everything is correct.
Unmatched payment. A payment exists with no corresponding booking. This means we charged a traveler's card but didn't record a booking. This is serious. The traveler paid for nothing.
Unmatched booking. A booking exists with no corresponding payment. This means a reservation was confirmed without collecting payment. This is also serious. We owe the travel provider for a seat we didn't collect money for.
A fourth category is amount mismatch: payment and booking are linked, but the amounts differ. This can happen with currency conversion rounding, partial refunds, or bugs in fare calculation.
Investigating mismatches
When the reconciliation job finds mismatches, it creates investigation records with all relevant context: the payment ID, booking ID, amounts, timestamps, and the webhook events associated with each.
Most mismatches resolve to one of a few root causes:
Timing. The payment webhook arrived but the booking confirmation webhook hasn't arrived yet. The booking exists in the travel provider's system but our database hasn't been updated. These resolve within minutes as the delayed webhook processes.
Partial refunds. The traveler changed their flight and received a partial refund. The payment amount no longer matches the current booking amount. The refund record explains the difference.
3D Secure edge cases. The traveler started 3DS verification, the payment was authorized, but the traveler abandoned the verification. Depending on timing, the authorization might expire or might have been captured before the abandonment was detected.
Webhook failures. The payment processor sent a webhook, our handler failed to process it (application error, deployment in progress), and the retry also failed. The payment exists on the processor's side but our system never learned about it.
Each investigation follows a runbook. Check webhooks. Check travel provider status. Check refund records. Most investigations close within hours. Rare cases require manual intervention by an engineer.
The zero tolerance policy

Our target for unreconciled payments is zero. Not "low." Zero.
Every dollar that enters the system must be accounted for. An unreconciled payment means a traveler was charged incorrectly, a booking exists in an undefined state, or money is sitting in a gap between systems. None of these are acceptable.
In practice, reconciliation runs find mismatches regularly. Most are timing-related and self-resolve. The ones that don't trigger alerts, and engineers investigate them within business hours. The goal is that by end of day, every mismatch is either resolved or has an active investigation with a known root cause.
Edge cases that break naive reconciliation
Simple matching by booking ID works for the happy path. Real payment flows have edge cases that naive reconciliation misses.
Failed-then-succeeded bookings. The first booking attempt fails (travel provider error). The payment was authorized but voided. The traveler tries again. The second attempt succeeds with a new payment. Naive reconciliation might try to match the voided payment to the successful booking.
Currency conversion. The traveler pays in their local currency. The booking is priced in the travel provider's currency. Exchange rates and processor fees create small differences between the payment amount and the booking amount. Reconciliation needs to account for expected currency conversion differences.
Split payments. Some bookings allow partial payment now and the remainder later. The booking has one amount, but there are two payment records that together equal the booking amount.
Cancellation and rebooking. The traveler cancels a booking and rebooks. There's a refund on the original payment and a new charge on a new payment. The reconciliation needs to match the refund to the original booking and the new charge to the new booking, not cross-match them.
Each of these edge cases required specific handling in our reconciliation logic. The happy path is easy. The edge cases are where the real engineering happens.
Audit trails
Reconciliation is also a compliance requirement. Regulators and auditors expect a clear paper trail showing how every payment maps to a service delivered.
Our audit trail records: who booked (user ID), when (timestamps for every state transition), how much (payment amount, fare amount, any adjustments), which payment method (last four digits, payment processor reference), which travel provider confirmation (booking reference, PNR), and the reconciliation status (matched, under investigation, resolved).
This audit trail is immutable. Records are append-only. Status changes add new entries rather than modifying existing ones. If a reconciliation match is later found to be incorrect, a correction entry is added, but the original match is never deleted.
Building automated reconciliation
If you're processing payments in a booking platform, implement reconciliation from day one.
Run reconciliation on a schedule, not on demand. A daily reconciliation run catches issues within 24 hours. By the time someone notices a problem manually, it might have been compounding for weeks.
Match on multiple criteria, not just booking ID. Amount matching, timing windows, and status checks catch edge cases that ID matching alone misses.
Alert on unmatched records immediately. An unmatched payment is a traveler who was charged incorrectly. An unmatched booking is revenue leakage. Both need prompt attention.
Handle edge cases explicitly. Don't assume the happy path covers everything. Refunds, currency conversion, split payments, and retry scenarios all need specific reconciliation logic.
Keep the audit trail immutable. You will need it for compliance, for customer support, and for debugging. Once written, a reconciliation record should never be modified or deleted.
Nowah is an AI travel agent that searches and books real flights and hotels through conversation — no filters, no thirty open tabs. Plan your next trip.