Billing Reconciliation Spec: Tolerance and Exceptions

Billing Reconciliation Spec: Tolerance and Exceptions
Spec Coding Editorial Team · Spec-first engineering notes

I have shipped two reconciliation systems and inherited four. The short version is this: if your internal ledger does not reconcile to exactly zero against itself, you have a bug, not a rounding problem. Tolerance is for the external world, where processors and banks round on their own clocks. The spec exists to draw that line and hold it under audit pressure.

Published on 2026-03-01 · Updated 2026-05-06 · 8 min read · Author: Spec Coding Editorial Team · Review policy: Editorial Policy

What "Day Closed" Actually Means

Most teams say the day is closed when the reconciliation job finishes. That is operator language, not spec language. In the spec, a closed day means three things are true: every order with a payment intent has a terminal state, every row in the internal payments table has a matched settlement line or an explicit exception reason, and the general ledger posting has been written and locked. Nothing moves money after close without a dated adjustment.

You cannot safely release funds to a merchant, issue a refund against yesterday, or recognize revenue until the day is closed in this stricter sense. I have watched a team treat "job finished" as close and then silently post negative adjustments the next morning because a processor pushed a late capture. The auditors noticed before the CFO did.

Two Tolerances, Not One

The single biggest spec mistake I see is a global tolerance of "one cent" applied everywhere. That is wrong. You need two tolerances, and they live in different parts of the pipeline.

Write both numbers into the spec and say why. Reviewers who have not lived through a finance escalation will push back on the zero-tolerance rule. Hold the line.

The $127.03 Problem

A concrete case from last year. A Stripe settlement line reads $127.00. The matching internal payment reads $127.03. The naive reconciler flags it "within tolerance" because the file had a $0.05 cushion, and moves on.

Three cents turned out to be a tax rounding bug. We were computing tax on the subtotal, rounding to the cent, then summing. The processor computed tax on the line total and rounded once. Over six months the drift reached about $4,200 across 140,000 transactions. Nothing caught it because every individual mismatch was "within tolerance." The spec rule that came out of this: tolerance applies to processor fees and FX. Tax, principal, and refunds match to zero or they go to the exception queue.

Exception Queues With Separate Playbooks

A single exception queue is a graveyard. I want four, each with its own on-call runbook and aging SLA:

Pairwise Sources, Not a Mega-Join

The spec should name exactly four reconciliation sources and describe the pairs between them. In my systems those are: the processor settlement file, the internal payments table, the internal orders table, and the tax system. Do not try to reconcile all four in one SQL statement. Run three pairwise jobs.

If all three pairs reconcile, the four-way reconciliation is implied. If one fails, you know exactly which interface is broken. A mega-join tells you there is a problem somewhere in a cloud of rows.

FX, Late Arrivals, and Chargebacks

Multi-currency adds three spec questions people try to dodge. Answer them explicitly: which rate, mid-market or processor-reported? At what timestamp, capture or settlement? Who absorbs slippage, the merchant or the platform? I default to processor-reported rate at settlement time, platform absorbs slippage up to 10 basis points, anything beyond routes to an FX exception queue.

Late arrivals are a separate beast. A charge authorized on day N can settle on day N+3 because of processor backlog. The spec must define the late-arrival window (I use 7 calendar days) and what happens on day 8: no longer eligible for auto-match, manual review required. Anything you do not define here, a junior engineer will define for you with a quiet timeout and a silent data-loss bug.

Chargebacks and disputes are not part of daily reconciliation. They move on a different clock and need their own flow against the dispute system. Lumping them into daily is how you get a "daily" job that never closes cleanly.

Who Can Zero Out $47?

Every reconciliation system eventually has a tiny residual that nobody can explain. The spec must answer: who has the authority to write it off? My rule: anything under $10 per month in aggregate can be written off by the reconciliation lead with a logged reason code. $10 to $500 requires finance approval. Over $500 requires an incident and a root-cause writeup before the write-off is booked. No write-off happens without a dated journal entry and a named approver. This is boring, and it is the single clause auditors care about most.

Observability That Actually Signals

Four metrics, published daily:

Acceptance Criteria

- Given a processor settlement file for 2026-04-16
  And all internal payments for that settlement date
  When the daily reconciliation job runs
  Then every settlement line matches an internal payment within $0.01
  And every internal payment is either matched or routed to a named exception queue
  And the internal payments-to-orders pair reconciles to exactly $0.00
  And the day is marked closed only if all three conditions hold

- Given an amount-diff exception of $0.03 on a principal line
  When the reconciler evaluates tolerance
  Then the exception is NOT auto-resolved
  And it is routed to the amount-diff queue with a 24-hour SLA

- Given a settlement line that arrives 8 calendar days after the capture date
  When the late-arrival matcher runs
  Then the line is flagged for manual review
  And it does NOT auto-match even if the amounts agree

These are the clauses I want signed off before anyone writes a line of SQL. If the reviewers cannot agree on them in a meeting, the system is not ready to be built, no matter how clean the architecture diagram looks.

Contract Review Packet to Copy

Use this when the work touches API behavior, schema, events, retries, or consumer expectations. The packet makes compatibility and release evidence explicit.

API contract review packet: Billing Reconciliation Spec: Tolerance and Exceptions

Decision to make:
- Writing a billing reconciliation spec: tolerance thresholds, exception queues, the daily close process, and why penny-level differences are usually a bug not a rounding artifact.

Owner check:
- Product owner:
- Engineering owner:
- QA or operations reviewer:

Scope boundary:
- In scope:
- Out of scope:
- Assumption that still needs approval:

Acceptance evidence:
- Test or fixture:
- Log, metric, or screenshot:
- Manual review step:

Contract boundary: no release without compatibility classification, consumer impact, retry behavior, and rollback notes.

Reviewer prompt:
- What would still be ambiguous to someone who missed the planning meeting?
- What evidence would make this safe enough to ship?

Editorial Review Note

Reviewed Apr 28, 2026. This update added a reusable artifact, checked the article against the related topic hub, and tightened the next-step links so the page works as a practical reference rather than a standalone essay.

Keywords: billing reconciliation spec · tolerance thresholds · exception queues · daily close · settlement matching · FX rounding · late arrivals · write-off approval

Topic Path

This article belongs to the API Contracts track. Start with the hub, then use the checklist, template, or tool below on a real project.

Editorial Note

Last reviewed Apr 28, 2026: examples, internal links, and reusable review blocks were checked for practical specificity.