/reconcile/*Automate bank-to-GL reconciliation. Upload bank statements and GL exports, trigger the 4-level matching engine, receive matched transaction pairs, and send review decisions (confirm/reject/escalate) back via the API.
Exact match
SQL equality on amount + reference
Fuzzy match
Weighted scoring — amount 45%, description 30%, date 15%, ref 10%
AI match
Ollama LLM explains edge-case pairings
Human review
Only genuine discrepancies reach the queue
A session groups one bank statement, one GL export, and all resulting matches. Create one per bank account per period.
| Field | Type | Description |
|---|---|---|
| namerequired | string | Human-readable label, e.g. 'May 2026 — GTBank' |
| period_start | date | null | Start of the statement period (ISO 8601 date) |
| period_end | date | null | End of the statement period (ISO 8601 date) |
/api/v1/reconcile/sessionsBoth upload endpoints accept multipart/form-data — not JSON. Use the console UI for browser-based uploads, or the code examples below for API integration. Parsing runs in the background; poll the statement/export record until parse_status === "done".
/reconcile/bank-statements| Field | Type | Description |
|---|---|---|
| filerequired | File | CSV, Excel (.xlsx/.xls), or PDF. Max 10 MB. |
| session_id | uuid | null | Attach to an existing session immediately. |
/reconcile/gl-exports| Field | Type | Description |
|---|---|---|
| filerequired | File | CSV or Excel (.xlsx/.xls). Max 10 MB. |
| session_id | uuid | null | Attach to an existing session immediately. |
| schema_profile_id | uuid | null | Apply a saved column-mapping profile. |
# Upload bank statement
curl -X POST http://localhost:8000/api/v1/reconcile/bank-statements \
-H "Authorization: Bearer $TOKEN" \
-F "file=@gtbank_may2026.csv" \
-F "session_id=$SESSION_ID"
# Upload GL export
curl -X POST http://localhost:8000/api/v1/reconcile/gl-exports \
-H "Authorization: Bearer $TOKEN" \
-F "file=@gl_export_may2026.xlsx" \
-F "session_id=$SESSION_ID" \
-F "schema_profile_id=$PROFILE_ID"Once both uploads have parse_status === "done", trigger reconciliation. The engine runs asynchronously — the session status moves to matching then completed.
/api/v1/reconcile/sessions/{session_id}/runFetch matched bank↔GL pairs. Filter by ?status=confirmed for fully reconciled pairs, ?status=pending for unreviewed engine matches.
| Field | Type | Description |
|---|---|---|
| id | uuid | Match pair UUID |
| bank_transaction_id | uuid | Bank-side transaction UUID |
| gl_transaction_id | uuid | GL-side transaction UUID |
| match_level | string | exact | rule_based | ai | manual |
| confidence_score | decimal|null | 0.00–1.00 — null for exact matches |
| confidence_breakdown | object | Per-signal scores: { amount, description, date, reference } |
| ai_explanation | string|null | Ollama explanation for AI-level matches |
| status | string | pending | confirmed | rejected | escalated |
| reviewed_by | uuid|null | User who reviewed this pair |
| reviewed_at | datetime|null | Review timestamp |
/api/v1/reconcile/sessions/{session_id}/matchesTip: append ?status=confirmed or ?status=pending to the URL in the Explorer tab to filter.
Confirm, reject, or escalate a matched pair. Confirming marks it as reconciled. Rejecting returns both transactions to the discrepancy queue. Escalating routes it to a senior reviewer without rejecting the match.
| Field | Type | Description |
|---|---|---|
| actionrequired | string | confirm | reject | escalate |
| note | string|null | Optional reason or reference (stored in audit log) |
/api/v1/reconcile/sessions/{session_id}/matches/{match_id}The discrepancy queue contains every transaction the engine could not match. Use ?source=bank for bank-side gaps or ?source=gl for GL-side gaps.
| Field | Type | Description |
|---|---|---|
| id | uuid | Transaction UUID |
| source | string | bank or gl |
| transaction_date | date|null | Date the transaction occurred |
| value_date | date|null | Value date (clearing date) |
| description | string|null | Narration or memo from the statement |
| reference | string|null | Bank reference or GL journal number |
| amount | decimal|null | Transaction amount (NGN or local currency) |
| currency | string | ISO 4217 code, default NGN |
| policy_reference | string|null | Policy or contract reference number |
| customer_name | string|null | Payer or counterparty name |
| match_status | string | Always unmatched for this endpoint |
/api/v1/reconcile/sessions/{session_id}/discrepanciesTip: append ?source=bank or ?source=gl to filter by side.
Aggregate stats: match rate, per-level breakdown, and unmatched counts for both sides. Use this to decide whether the session is ready to close.
/api/v1/reconcile/sessions/{session_id}/reportSkip file uploads — POST transactions as structured JSON directly into a session. Useful for API-driven integrations where your system already has parsed transaction data.
/reconcile/sessions/{session_id}/transactions| Field | Type | Description |
|---|---|---|
| sourcerequired | string | "bank" or "gl" |
| itemsrequired | array[object] | Array of transaction objects |
| items[].transaction_daterequired | string | ISO 8601 date |
| items[].amountrequired | number | Transaction amount |
| items[].description | string | Narrative/description |
| items[].reference | string | Reference or cheque number |
| items[].currency | string | ISO 4217 code — defaults to "NGN" |
curl -X POST http://localhost:8000/api/v1/reconcile/sessions/{session_id}/transactions \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source": "bank",
"items": [
{ "transaction_date": "2026-05-01", "amount": 125000.00, "description": "TRANSFER TO VENDOR", "reference": "TRF/2026/0001" },
{ "transaction_date": "2026-05-02", "amount": -45000.00, "description": "BANK CHARGES", "reference": "CHG/2026/001" }
]
}'