Reconciliation
Run bank-to-ledger reconciliation and read the current matching status.
Endpoints
GET/api/v1/companies/:companyId/reconciliation/bank/status: Bank-reconciliation health snapshot.POST/api/v1/companies/:companyId/reconciliation/bank/run: Run the bank-reconciliation matcher.
GET /api/v1/companies/:companyId/reconciliation/bank/status
reconciliation.bank.status · scope transactions:read
Bank-reconciliation health snapshot.
Returns matched / unmatched counts and the balance delta between the bank ledger and the GL for the requested window. Optional ?date_from / ?date_to (default: company history).
Use when: You're building a dashboard widget, an audit report, or a pre-close check that needs to know how many bank transactions are still unbooked.
Don't use for: Running the matcher: that's POST /reconciliation/bank/run. Per-transaction detail: use the transaction list with ?status=unbooked.
Pitfalls
- A non-zero difference is normal between sync runs (uncleared cheques, in-flight transfers). Investigate only if it persists across reconciliations.
- difference compares against gl_1930_period_movement (movement excl. opening balance), NOT gl_1930_balance. Do not display gl_1930_balance next to difference.
- is_reconciled means |difference| < 0.01 for the window, an aggregate check, not a per-transaction guarantee.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Example response
{
"data": {
"bank_transaction_total": 48150,
"gl_1930_balance": 98150,
"gl_1930_period_movement": 48150,
"gl_1930_opening_balance": 50000,
"gl_1930_correction_adjustment": 0,
"difference": 0,
"is_reconciled": true,
"matched_count": 142,
"unmatched_transaction_count": 3,
"unmatched_gl_line_count": 2
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/reconciliation/bank/run
reconciliation.bank.run · scope transactions:write
Run the bank-reconciliation matcher.
Walks all unbooked bank transactions in the requested date range and pairs them with open GL lines (1930-side) by amount + date proximity. Applies confirmed matches by setting transactions.journal_entry_id (the GL row already exists). Dry-runnable.
Use when: You want to auto-match outstanding bank transactions against existing journal entries: typically as the closing step of a sync. Dry-run first to inspect proposed matches.
Don't use for: Creating new journal entries: this only links bank transactions to existing GL lines. Matching to invoices: use :match-invoice or :match-supplier-invoice for explicit invoice payments.
Pitfalls
- date_from / date_to default to the company's full bank history if omitted. Specify a window for predictable performance.
- account_number defaults to 1930. Multi-account companies must pass the BAS code of the account they are reconciling (e.g. 1932 for a EUR account), or it silently reconciles 1930.
- Idempotency-Key is mandatory.
- Without confidence_threshold, a non-dry run applies EVERY match found, including fuzzy ones at confidence 0.75. Pass confidence_threshold (0.9 recommended, matching gnubok_auto_match_period) for unattended runs, or dry-run first and review matches.confidence before applying. Matches below the threshold are returned but not applied (skipped_below_threshold counts them).
- The 366-day window bound only applies when BOTH date_from and date_to are set; a single-sided or absent window scans full history.
Risk: medium · Idempotent: yes · Reversible: no · Dry-run supported: yes
Example request
{
"date_from": "2026-05-01",
"date_to": "2026-05-31",
"confidence_threshold": 0.9
}
Example response
{
"data": {
"matches": [],
"applied": 0,
"errors": 0,
"skipped_below_threshold": 0
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}