---
title: "Poll a reconciliation"
method: GET
path: "/api/v1/reconciliations/{id}"
tags: ["ReconcileIQ"]
---

# Poll a reconciliation

`GET /api/v1/reconciliations/{id}`

## Request

- Base URL: `https://api.reconcileiq.com`
- URL: `https://api.reconcileiq.com/api/v1/reconciliations/{id}`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-API-Key`

Fetches reconciliation status and, once finished, the result. `status` is one of `queued`, `processing`, `completed`, `failed`. The `result` object is present only when `status` is `completed`; the `error` object only when `status` is `failed`. **A reconciliation that ran but could not be balance-verified is intended, documented behaviour, not an error**: it still completes with `status: completed`, `result.summary.verified: false`, `result.verification.is_verified: false` with a human-readable `result.verification.reason`, and `credits_charged: 0` (top-level and inside `result`) — nothing is billed. A verified run carries `credits_charged: <n>` (n = bank rows). Always branch on `verification.is_verified` / `credits_charged`, never on `status` alone. Visible only to the owning user; requires scope `reconcileiq:read` OR being the exact key that created it. Non-owned or unknown ids return 404 (no existence leak).

## Path parameters

- `id` string, required

## Response `200`

Reconciliation state.

- ReconciliationState
  - `reconciliation_id` string, required
  - `status` 'queued' | 'processing' | 'completed' | 'failed', required
  - `created_at` string, date-time, required
  - `completed_at` string, date-time: Present once terminal.
  - `credits_charged` integer: Present once the run is completed. Credits charged for this reconciliation: 0 if it could not be balance-verified (intended behaviour, never charged), or n = bank rows if verified. Mirrors result.credits_charged.
  - `result` ReconciliationResult: Completed reconciliation result. The verification contract is always present and machine-readable: summary.verified, top-level credits_charged, and verification.{is_verified,expected_net_discrepancy,actual_net_effect,reason}. A run that completes but cannot be balance-verified is intended behaviour (status stays 'completed') and is not charged (credits_charged = 0). Branch on verification.is_verified / credits_charged, not on status alone.
    - `summary` object
      - `bank_rows` integer
      - `book_rows` integer
      - `matched_count` integer
      - `missing_from_books_count` integer
      - `remove_from_books_count` integer
      - `verified` boolean
    - `credits_charged` integer: Credits charged for this run. 0 unless the run completed AND was balance-verified (an unverifiable reconciliation is never charged). Mirrored at the top level of the GET response too.
    - `missing_from_books` NormalisedRow[]
      - `date` string
      - `amount` unknown
      - `description` string
    - `remove_from_books` NormalisedRow[]
      - `date` string
      - `amount` unknown
      - `description` string
    - `verification` object
      - `is_verified` boolean
      - `expected_net_discrepancy` number, nullable: Net discrepancy implied by the two datasets (total bank minus total book). null if the engine could not compute a balance check.
      - `actual_net_effect` number, nullable: Net effect of the unreconciled items (sum of missing_from_books minus sum of remove_from_books). null if not computable.
      - `reason` string: Stable human-readable explanation of the verification outcome (verified, or why it could not be balance-verified and therefore was not charged). Always present on a completed run.
    - `matched` object[]: Present only when options.includeMatched was true (capped at 500 pairs).
      - `bank` NormalisedRow
        - `date` string
        - `amount` unknown
        - `description` string
      - `book` NormalisedRow
        - `date` string
        - `amount` unknown
        - `description` string
      - `date_difference` integer, nullable
  - `error` object: Present only when status is failed.
    - `code` string
    - `message` string
    - `details` unknown

## Other responses

- `401`: Missing, invalid, or revoked API key.
- `403`: Authenticated but the key lacks the required scope.
- `404`: Resource not found, or not visible to the caller (no existence leak).
- `429`: Per-key rate limit exceeded (120 requests / 60s window).

---

[API](https://skmtc.dev/reconcileiq/apis/reconcileiq-public-api.md) · [All operations](https://skmtc.dev/reconcileiq/apis/reconcileiq-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/reconcileiq/apis/reconcileiq-public-api/revisions/5401beef8653?raw)
