How-to guidesView as Markdown ↗

Reconcile money in

In plain terms

Each deposit in your bank feed is matched to the invoice it pays, and the invoice is marked paid on the day the money landed. Deposits that aren't invoice payments, like interest, are filed to a category instead. Biloh learns who each payer is for next time, and a wrong match can be undone.

Read the worklist, confirm the deposits whose candidates are clear, decide the rest. A deposit with an invoice candidate is matched with match_bank_credit_to_invoice, which records the payment on the bank's real value date and learns the payer's bank alias. A deposit that is not invoice income (interest, a marketplace payout, an old-system payment) is triaged into a reporting category instead, never matched. A match is reversible with unmatch_reconciliation.

At a glance

Who it's for:
Business owners and the people who run the office
What a person approves:
Biloh never moves money: its tools record payments that already happened, and a pay-now link opens the business's own Stripe checkout for its client. · Every change is recorded, with who made it and when.
Checked against the code:
2026-10-29

Step 1: read the worklist

"What deposits are waiting?"

triage_bank_feed in worklist mode returns every untriaged line. For each deposit it carries candidates: the outstanding invoices it could be paying, ranked with a confidence score, a band of auto_suggest or needs_review, the evidence for the match in reasons, and any cautions in a separate warnings list. Read the candidates before you do anything else. A deposit with candidates should be matched to an invoice, not filed into a category, even when a payer rule has proposed one.

What counts as evidence, strongest first:

  1. The invoice number printed in the bank text. It outranks everything and surfaces the invoice even when the amount differs (a part payment, a fee deduction, a split).
  2. An exact amount against what an outstanding invoice still owes, then against its total. A part-paid invoice's balance payment ($82.00 on a $132.00 invoice with $50.00 already paid) is a match on the amount owed.
  3. A known payer. Both the client's legal and trading names are matched, because banks print the account holder's legal name, plus any bank alias learned from a previous match.
  4. A plausible date relative to the invoice.

A caution is never a point in a match's favour. already_paid means that invoice is settled and is only offered because the bank text names it; amount_mismatch means the deposit equals neither what the invoice still owes nor its total. A candidate with any warning is never auto-suggested.

Step 2: confirm the clear ones

"Match the high-confidence ones."

For each deposit whose top candidate is auto_suggest, match_bank_credit_to_invoice does four things: records the customer payment against the invoice on the bank's real value date through the audited payment gateway, marks the deposit reconciled and linked to that payment, transitions the invoice to paid or partially paid, and learns the client's bank alias so this payer is recognised next time. On screen, "Confirm high-confidence matches" does the same in bulk, gated by finance.reconcile.bulk_confirm_min_score_pct (default 95). The bulk confirm also requires the deposit to equal what the invoice still owes, so a deposit equal to a part-paid invoice's full total (an overpayment) waits for a person.

The payment is keyed to the deposit, so the same deposit can never pay twice; a second attempt is refused with already_matched rather than minting a duplicate that would become a spurious client credit.

Step 3: decide the rest

For deposits with no candidate, or a candidate you reject, the deposit is one of:

  • Invoice income the ranker could not see. Name the invoice yourself and match it.
  • A payment you already recorded (cash taken on site, an EFT keyed in by hand). link_bank_credit_to_existing_payment links the deposit to that payment without creating new money.
  • Not invoice income. Interest, a marketplace payout, a refund from a supplier, a payment for work invoiced in your previous system. Triage it into a reporting category with triage_bank_feed in decide mode (or triage_bank_transaction for one line). A category's treatment decides whether it reaches the P&L as income or is excluded. Never match a non-invoice deposit to an invoice to tidy the list; it would misstate income.
  • Your own money arriving from another account. Pair it as a transfer; see envelopes.
  • A cash or non-invoice receipt you want booked as a receipt anyway. "Record as a cash receipt" on the Reconcile screen books it against the client named in finance.reconcile.cash_receipt_client_id.

When a decision applies to every future line from the same payer, add create_rule to the decision. create_bank_payer_rule remembers the description substring and the category, and apply_bank_payer_rules runs after each import. A rule never auto-triages a line whose amount equals an outstanding invoice total; that line is offered for matching instead.

Step 4: check the story

"Show me everything that looks like it came from that childcare client."

get_client_money_story resolves the client by name, sweeps the feed's credits for deposits that look like theirs (legal and trading names, site contact names, any aliases), and lays them beside the client's invoices and recorded payments. It is the read for "did they pay, and when?".

Reversing a match

unmatch_reconciliation voids the payment the match created, which unwinds its allocation and recomputes the invoice off paid, then frees the deposit back to the worklist. Nothing is deleted; the audit log keeps both acts.

Related

Under the hood

Source files this page is checked against:

  • lib/mcp/tools/finance/get-reconciliation-suggestions.ts
  • lib/mcp/tools/finance/match-bank-credit-to-invoice.ts
  • lib/mcp/tools/finance/link-bank-credit-to-existing-payment.ts
  • lib/mcp/tools/finance/bulk-triage-bank-transactions.ts
  • lib/mcp/tools/finance/triage-bank-feed.ts

Raw markdown: /docs-md/how-to/reconcile-money-in

Last updated 2026-10-29