Reconciliation

Every settlement day produces a batch you can prove: a report whose lines sum to its payout, with your payment id and order reference on every row. This page covers the batch objects, the report columns, the id chain from bank statement down to payment, and the daily workflow that closes the books.

The id chain

Reconciliation is following ids downward — from the money that arrived to the orders that earned it. Every link in the chain is a field in the API:

bank statement credit +19,141.90 EUR transfer carries the payout id payout po_6Fn1mS8vQ2Xc · 1914190 settlement_report field settlement report stl_2026_07_16_eu01 · net 1914190 payment_id + reference on every row payment pay_8Q2mX4nT1cVb · ord_9f21_0716 provider reference provider's own tx id network reference scheme ARN / RRN held by Finscale per attempt — resolved for you when quoted
IdPrefix / formIssued byWhere you see it
Payment idpay_FinscaleThe API, every webhook, and the payment_id column of every report row.
Merchant referenceyours (ord_9f21_0716)YouEchoed onto the payment and every report row — the join key to your orders table.
Settlement report idstl_FinscaleNames the batch; linked from the payout's settlement_report field.
Payout idpo_FinscaleGET /v1/payouts, and on the bank transfer that lands in your account.
Provider idprov_FinscaleThe provider column and field — opaque, stable, never a company name.
Provider referenceprovider-specificProviderNot an API field. Finscale stores it per attempt; it surfaces in dispute evidence and support lookups.
Network referenceARN / RRNCard networkNot an API field. Used when an issuer or bank quotes one — Finscale resolves it to a pay_ id for you.

The practical rule: everything you automate joins on payment_id or reference. The provider and network references exist so that when an outside party quotes theirs — an issuer in a dispute, a bank tracing a transfer — the chain still resolves to one payment. You never store them yourself.

Settlement batches

A settlement report is the batch object: it covers a settlement day, carries normalized totals, and flips to ready when its CSV is generated. Reports are produced per provider per settlement day — plus a consolidated report with "provider": null that spans all of them. Whatever the provider, the shape is identical:

GET /v1/settlement_reports/stl_2026_07_16_eu01
{
  "id": "stl_2026_07_16_eu01",
  "object": "settlement_report",
  "period_start": "2026-07-15",
  "period_end": "2026-07-16",
  "provider": "prov_eu_acq_01",
  "currency": "EUR",
  "status": "ready",
  "totals": {
    "gross": 2041800,
    "refunds": -96400,
    "fees": -31210,
    "net": 1914190
  },
  "transaction_count": 412,
  "payout": "po_6Fn1mS8vQ2Xc",
  "livemode": false,
  "created_at": "2026-07-16T04:00:00Z"
}

Three properties make batches provable:

  • The totals identity. gross + refunds + fees = net, always — refunds and fees are negative. Here: 2041800 − 96400 − 31210 = 1914190.
  • The payout link. payout names the payout whose amount equals totals.net. Money in the report and money on the bank statement are the same number.
  • Immutability. A ready report never changes. Corrections arrive as itemized lines in a later report, per the ledger's append-only rule.

List batches with GET /v1/settlement_reports (newest first, cursor pagination); filter to one provider with ?provider=prov_eu_acq_01. A report in status: generating has no CSV yet — wait for the settlement.report.ready event rather than polling.

Report columns

Fetch the transaction-level export with Accept: text/csv. One row per payment, refund, and fee line; amounts in minor units:

ColumnContents
payment_idThe payment behind this line — always present. Refund lines carry the refunded payment's id.
referenceYour order reference, echoed from the payment. The join key to your side.
methodMethod code (ideal, card, …).
providerOpaque provider id that processed the transaction.
grossMinor units. Negative on refund lines.
feeFee for this line, minor units — always negative. What it's made of: fee concepts.
netgross + fee. The column that sums to the payout amount.
currencyISO 4217.
settled_atWhen the line settled (ISO 8601, UTC).
GET /v1/settlement_reports/stl_2026_07_16_eu01 — Accept: text/csv
curl https://api.finscale.dev/v1/settlement_reports/stl_2026_07_16_eu01 \
  -H "Authorization: Bearer sk_test_51FinscaleDemo…" \
  -H "Accept: text/csv"

payment_id,reference,method,provider,gross,fee,net,currency,settled_at
pay_8Q2mX4nT1cVb,ord_9f21_0716,ideal,prov_eu_acq_01,4900,-64,4836,EUR,2026-07-16T04:00:00Z
pay_3Xw9cR5pLm,ord_9f22_0716,card,prov_eu_acq_01,12900,-206,12694,EUR,2026-07-16T04:00:00Z
pay_8Q2mX4nT1cVb,ord_9f21_0716,ideal,prov_eu_acq_01,-1500,-25,-1525,EUR,2026-07-16T04:00:00Z
# … 409 more rows. sum(net) = totals.net = payout amount = 1914190

Row three is a refund line: negative gross, the refunded payment's payment_id, and the original reference — a partial refund of €15.00 against the €49.00 order. The CSV is idempotent: re-download it any time and you get the same bytes.

The daily workflow

Five steps, one per morning, fully automatable:

  1. Receive the batch

    Subscribe to settlement.report.ready via webhooks — it fires when a report's CSV is generated, shortly after the settlement day closes. (Fallback: poll GET /v1/settlement_reports and pick up reports with status: ready you haven't processed.)

  2. Pull the CSV

    GET /v1/settlement_reports/{id} with Accept: text/csv. Store it — it's your audit artifact for the day.

  3. Prove the batch

    Assert the three-way tie: sum(net) over the rows = totals.net = the linked payout's amount. If this holds, the batch is internally consistent before you look at a single order.

  4. Join to your orders

    Match rows on reference (or payment_id if you stored it at creation — you should). Mark matched orders as settled with their settled_at and net amount. Refund rows net against the same order.

  5. Tie the bank statement

    When the payout lands (its arrival_date), match the single credit of €19,141.90 on your bank statement to po_6Fn1mS8vQ2Xc. One transfer, one number, already proven equal to the batch.

Webhook: settlement.report.ready
{
  "id": "evt_7k1M3nQwB8Xd",
  "object": "event",
  "type": "settlement.report.ready",
  "data": {
    "object": {
      "id": "stl_2026_07_16_eu01",
      "object": "settlement_report",
      "status": "ready",
      "currency": "EUR",
      "totals": { "gross": 2041800, "refunds": -96400, "fees": -31210, "net": 1914190 },
      "transaction_count": 412,
      "payout": "po_6Fn1mS8vQ2Xc"
    }
  },
  "created_at": "2026-07-16T04:00:02Z",
  "livemode": false
}
Events point, they don't carry Webhook delivery order isn't guaranteed. Treat the event as a trigger to fetch the report by id — never as the data itself, and never assume it arrives before the payout does.

When a line doesn't match

A healthy recon run classifies its exceptions instead of staring at them:

SymptomAlmost alwaysDo
Order captured yesterday, not in today's report Timing — the line hasn't settled yet. Slower rails (e.g. direct debit) settle later than cards. Check it's still in pending on GET /v1/balance; expect it in a later batch. Alert only past the method's normal window.
Row with negative gross you didn't expect A refund — possibly issued by support outside your order flow. Join on payment_id; list the payment's refunds with GET /v1/refunds?payment=….
Balance moved but no report line explains it A dispute — disputed amounts are held from available when the dispute opens, itemized on the dispute object rather than as a report row. Reconcile disputes from GET /v1/disputes and dispute.* events, separately from the settlement batch.
Row matches no order at all A payment created outside your order system — a retried checkout, a manual charge, a stale reference. Fetch the payment by id; its metadata and provider_attempts usually identify the source.
sum(net) ≠ payout amount Should not happen — the identity is enforced by the ledger. Re-download the CSV (partial file?), re-check your parser handles negative values. If it genuinely fails, contact your Finscale representative with the report id.
  • Settlement & payouts — schedules, reserves, and what's inside the fee column.
  • Ledger — why the totals identity can't drift.
  • Balance — where unsettled lines live in the meantime.
  • Webhooks — signatures, retries, and ordering.