# Reconciliation

> Bank statement reconciliation with AI-assisted matching.

- **Category:** Guides
- HTML version: https://www.ondayzero.com/docs/guides/reconciliation
- All guides: https://www.ondayzero.com/docs/guides/index.md
- Endpoint catalog: https://www.ondayzero.com/docs/reference/index.md

---

# Reconciliation

Verify that your ledger balances match your bank statements. DayZero uses AI to parse statements and deterministic matching to suggest transaction pairs.

## How It Works

1. **Upload** a bank statement (PDF, CSV, or Excel) for a ledger and statement period
2. **AI parses** the statement into structured transaction lines
3. **Matching engine** pairs statement lines with ledger transactions in that period
4. **Review** suggested matches — approve, reject, or match manually
5. **Complete** the reconciliation to lock it and mark the transactions reconciled

| Status | Meaning |
|--------|---------|
| `pending` | Created, processing not started yet |
| `processing` | Parsing the statement and running matching |
| `ai_matched` | Suggestions ready for review |
| `in_review` | A user is approving/rejecting matches |
| `completed` | Locked; matched transactions flagged `reconciled` |
| `failed` | Processing failed — `POST …/start` retries it |

## Create a Reconciliation

### From File Upload

The ledger and statement period are query parameters; the file is the multipart body:

```bash
curl -X POST "https://api.ondayzero.com/api/v1/reconciliations/upload?ledger_id=BANK_LEDGER_UUID&start_date=2026-03-01&end_date=2026-03-31&opening_balance=1250000&closing_balance=1387550" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID" \
  -F "file=@statement.pdf"
```

`ledger_id`, `start_date` and `end_date` are required. `opening_balance` / `closing_balance` are in cents and let DayZero validate the reconciliation before completion; when the parser finds balances on the statement itself, those are stored too. Files up to 50 MB are accepted. Processing starts automatically unless you pass `auto_start=false`.

### From an Uploaded S3 Key

If the statement was already uploaded (see [File Uploads](/docs/guides/uploads)):

```bash
curl -X POST "https://api.ondayzero.com/api/v1/reconciliations" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "ledger_id": "BANK_LEDGER_UUID",
    "start_date": "2026-03-01",
    "end_date": "2026-03-31",
    "source_type": "manual",
    "statement_s3_key": "statements/march-2026.pdf",
    "statement_filename": "march-2026.pdf",
    "opening_balance": 1250000,
    "closing_balance": 1387550
  }'
```

`source_type` is `manual` (uploaded statement — `statement_s3_key` required) or `plaid` (reconcile against the synced bank feed for the period, no file needed).

## Review Matches

Poll `GET /api/v1/reconciliations/{reconciliation_id}` until `status` is `ai_matched`. The response lists statement lines with their suggested ledger transactions and confidence scores.

```bash
# Approve one match (one statement line ↔ one or more ledger transactions)
curl -X POST "https://api.ondayzero.com/api/v1/reconciliations/{reconciliation_id}/matches/approve" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID" \
  -H "Content-Type: application/json" \
  -d '{"statement_txn_id": "STATEMENT_LINE_ID", "ledger_txn_ids": ["TRANSACTION_UUID"]}'

# Approve everything the engine is confident about
curl -X POST "https://api.ondayzero.com/api/v1/reconciliations/{reconciliation_id}/matches/bulk-approve" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID" \
  -H "Content-Type: application/json" \
  -d '{"min_confidence": 0.9}'
```

`…/matches/reject` and `…/matches/bulk-reject` discard suggestions; approving with your own `ledger_txn_ids` creates a manual match. `POST …/explain-matches` asks the AI to explain why each pair was suggested.

## Check the Balance

```bash
curl "https://api.ondayzero.com/api/v1/reconciliations/{reconciliation_id}/balance" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID"
```

Reports whether *opening balance + net matched transactions = closing balance*, and the difference if not.

## Complete a Reconciliation

After reviewing matches:

```bash
curl -X POST "https://api.ondayzero.com/api/v1/reconciliations/{reconciliation_id}/complete" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID" \
  -H "Content-Type: application/json" \
  -d '{"skip_balance_check": false}'
```

Completion validates the balance (pass `"skip_balance_check": true` to override), locks the reconciliation, and marks matched transactions as `reconciled`. `GET …/pdf` downloads the reconciliation report once completed.

## List Reconciliations

```bash
curl "https://api.ondayzero.com/api/v1/reconciliations?status=in_review&limit=20" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID"
```

This endpoint uses `limit` / `offset` paging rather than cursors.
