# Import a bank statement

> The bank_statements.imports.create operation: POST /api/v1/bank_statements/imports in the REST API and the bank_statements_imports_create tool in the MCP server.

Imports a bank statement of the legal entity's account as CSV and matches its incoming transfers to open
invoices of every brand of the entity (send_invoice invoices carry their number as the payment reference).

- The file needs a header row and, per booked transaction, a booking date, a signed amount (incoming money is
  positive), the reference and optionally the counterparty and a currency. Columns are recognised by common
  header names (English, German, Croatian) or named in `columns`; dates as `YYYY-MM-DD`, `DD.MM.YYYY` or with
  slashes (`dateFormat` required); `decimalSeparator` `.` or `,`. A file with an unreadable row is refused
  whole (`bank_statement_invalid`, rows in `details.errors`); at most 2,000 transactions per file.
- The same file twice is refused; lines an earlier import has (overlapping statements) are skipped and
  counted as `duplicates`.
- A line whose reference names exactly one open invoice and whose amount is that invoice's amount due pays
  it at once, as `invoices.mark_paid` would on the booking date (`invoice.paid`; a past-due subscription
  recovers). Every other line waits in the review queue (`bank_statements.lines.list` with
  `matchStatus=partial,unmatched`) with the reason and the invoices it may pay.

Financial tier: it records payments. Dry-run it first to see the matches without saving anything. CAMT.053
files arrive in a later version.

## At a glance

- **REST:** `POST /api/v1/bank_statements/imports`
- **MCP tool:** `bank_statements_imports_create`
- **Operation ID:** `bank_statements.imports.create`
- **Scope:** Legal entity
- **Permission:** `invoices:write`
- **Risk tier:** `financial`
- **Dry run:** Supported (send `Ownrecord-Dry-Run: true`)
- **Idempotency:** `Idempotency-Key` required
- **Modes:** Test and live
- **Authentication:** API key with the `invoices:write` scope
- **Since:** M5

## Request

### Body

A JSON object (`Content-Type: application/json`):

| Field                  | Type                                                          | Required | Description                                                                                                                                                  |
| ---------------------- | ------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `csv`                  | string                                                        | Yes      | The statement as CSV text: a header row, then one booked transaction per row (booking date, signed amount, reference, optionally counterparty and currency). |
| `currency`             | string                                                        | Yes      | The bank account's currency (rows without a currency column).                                                                                                |
| `filename`             | string                                                        | No       |                                                                                                                                                              |
| `delimiter`            | one of `,`, `;`, `	`                                          | No       | `,`, `;` or a tab. Default: detected from the header.                                                                                                        |
| `dateFormat`           | one of `YYYY-MM-DD`, `DD.MM.YYYY`, `DD/MM/YYYY`, `MM/DD/YYYY` | No       | Default: `YYYY-MM-DD`, or `DD.MM.YYYY` when the dates have dots. Dates with slashes need it.                                                                 |
| `decimalSeparator`     | one of `.`, `,`                                               | No       | Default: `,` in `;`-separated files, else `.`.                                                                                                               |
| `columns`              | object                                                        | No       | Header names to use when the recognised ones don't fit (e.g. `{ reference: "Remittance" }`).                                                                 |
| `columns.bookingDate`  | string                                                        | No       |                                                                                                                                                              |
| `columns.amount`       | string                                                        | No       |                                                                                                                                                              |
| `columns.reference`    | string                                                        | No       |                                                                                                                                                              |
| `columns.counterparty` | string                                                        | No       |                                                                                                                                                              |
| `columns.currency`     | string                                                        | No       |                                                                                                                                                              |

## Response

`201 Created` with a JSON object:

| Field                          | Type                                                                  | Description                                                                                      |
| ------------------------------ | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `id`                           | `bsi_…` ID                                                            |                                                                                                  |
| `object`                       | `"bank_statement_import"`                                             |                                                                                                  |
| `entityId`                     | `ent_…` ID                                                            |                                                                                                  |
| `livemode`                     | boolean                                                               |                                                                                                  |
| `format`                       | `"csv"`                                                               |                                                                                                  |
| `filename`                     | string or null                                                        |                                                                                                  |
| `currency`                     | string                                                                |                                                                                                  |
| `lineCount`                    | integer                                                               | Transactions in the file.                                                                        |
| `counts`                       | object                                                                | Lines by match status (kept current as lines are matched or ignored); `duplicates` were skipped. |
| `counts.imported`              | integer                                                               |                                                                                                  |
| `counts.duplicates`            | integer                                                               |                                                                                                  |
| `counts.matched`               | integer                                                               |                                                                                                  |
| `counts.partial`               | integer                                                               |                                                                                                  |
| `counts.unmatched`             | integer                                                               |                                                                                                  |
| `counts.ignored`               | integer                                                               |                                                                                                  |
| `from`                         | string or null                                                        |                                                                                                  |
| `to`                           | string or null                                                        |                                                                                                  |
| `options`                      | object                                                                |                                                                                                  |
| `options.delimiter`            | one of `,`, `;`, `	`                                                  |                                                                                                  |
| `options.dateFormat`           | one of `YYYY-MM-DD`, `DD.MM.YYYY`, `DD/MM/YYYY`, `MM/DD/YYYY` or null |                                                                                                  |
| `options.decimalSeparator`     | one of `.`, `,` or null                                               |                                                                                                  |
| `options.columns`              | object                                                                |                                                                                                  |
| `options.columns.bookingDate`  | string or null                                                        |                                                                                                  |
| `options.columns.amount`       | string or null                                                        |                                                                                                  |
| `options.columns.reference`    | string or null                                                        |                                                                                                  |
| `options.columns.counterparty` | string or null                                                        |                                                                                                  |
| `options.columns.currency`     | string or null                                                        |                                                                                                  |
| `importedBy`                   | object                                                                |                                                                                                  |
| `importedBy.type`              | string                                                                |                                                                                                  |
| `importedBy.id`                | string                                                                |                                                                                                  |
| `version`                      | integer                                                               |                                                                                                  |
| `createdAt`                    | date-time                                                             |                                                                                                  |
| `updatedAt`                    | date-time                                                             |                                                                                                  |
| `paidInvoiceIds`               | array of `inv_…` ID                                                   | The invoices this import paid automatically.                                                     |

## Examples

### Import a statement: one transfer pays its invoice, two wait for review

```http
POST /api/v1/bank_statements/imports
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "csv": "Booking date,Amount,Reference,Counterparty\n2026-11-04,1200.00,GEN-2026-000131,Alpen Apps GmbH\n2026-11-04,450.00,Invoice 2026-000132,Berg Software AG\n2026-11-04,-12.50,Account fee,Bank",
  "currency": "EUR",
  "filename": "statement-2026-11-04.csv"
}
```

Response (`201`):

```json
{
  "id": "bsi_01m4tx5nj8emwbyf7y9vcf48ba",
  "object": "bank_statement_import",
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47zm",
  "livemode": false,
  "format": "csv",
  "filename": "statement-2026-11-04.csv",
  "currency": "EUR",
  "lineCount": 3,
  "counts": {
    "imported": 3,
    "duplicates": 0,
    "matched": 1,
    "partial": 0,
    "unmatched": 2,
    "ignored": 0
  },
  "from": "2026-11-04",
  "to": "2026-11-04",
  "options": {
    "delimiter": ",",
    "dateFormat": null,
    "decimalSeparator": null,
    "columns": {
      "bookingDate": "Booking date",
      "amount": "Amount",
      "reference": "Reference",
      "counterparty": "Counterparty",
      "currency": null
    }
  },
  "importedBy": {
    "type": "user",
    "id": "usr_01m4tx5nj8emwbyf7y9vcf47zk"
  },
  "version": 2,
  "createdAt": "2026-11-05T09:00:00.000Z",
  "updatedAt": "2026-11-05T09:00:00.000Z",
  "paidInvoiceIds": ["inv_01m4tx5nj8emwbyf7y9vcf47zn"]
}
```

## Errors

| Code                                                                                                               | HTTP status | When                                                                                             | What to do                                                                                                                                              |
| ------------------------------------------------------------------------------------------------------------------ | ----------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed)                             | 400         | A field is malformed or out of range.                                                            | Fix the fields listed in `details.issues` and send the request again.                                                                                   |
| [`bank_statement_invalid`](https://ownrecord.io/docs/reference/errors.md#bank_statement_invalid)                   | 400         | The CSV can't be read: a missing column, a bad date or amount (`details.errors` lists the rows). | Fix the rows listed in `details.errors` (or name the columns, date format and decimal separator) and import the whole file again. Nothing was imported. |
| [`bank_statement_already_imported`](https://ownrecord.io/docs/reference/errors.md#bank_statement_already_imported) | 409         | This exact file was imported before (`details.import_id`).                                       | Open the earlier import (`details.import_id`) instead. Overlapping statements with different files are fine: their common lines are skipped.            |
| [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict)                               | 409         | An invoice or line changed at the same time (another import or payment); retry.                  | Fetch the object again and retry the change.                                                                                                            |
| [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied)                             | 403         | The credentials can't record invoice payments for this entity (`invoices:write`, finance).       | Ask an organization owner or admin for a role or scope that allows this action.                                                                         |

It can also return these [common errors](https://ownrecord.io/docs/reference.md#common-errors): [`api_version_unsupported`](https://ownrecord.io/docs/reference/errors.md#api_version_unsupported), [`invalid_json`](https://ownrecord.io/docs/reference/errors.md#invalid_json), [`unsupported_media_type`](https://ownrecord.io/docs/reference/errors.md#unsupported_media_type), [`authentication_required`](https://ownrecord.io/docs/reference/errors.md#authentication_required), [`api_key_invalid`](https://ownrecord.io/docs/reference/errors.md#api_key_invalid), [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required), [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found), [`idempotency_key_required`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_required), [`idempotency_key_reused`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_reused), [`idempotency_in_progress`](https://ownrecord.io/docs/reference/errors.md#idempotency_in_progress), [`approval_required`](https://ownrecord.io/docs/reference/errors.md#approval_required), [`approval_rejected`](https://ownrecord.io/docs/reference/errors.md#approval_rejected), [`approval_expired`](https://ownrecord.io/docs/reference/errors.md#approval_expired), [`stale_approval`](https://ownrecord.io/docs/reference/errors.md#stale_approval), [`approval_policy_denied`](https://ownrecord.io/docs/reference/errors.md#approval_policy_denied), [`rate_limited`](https://ownrecord.io/docs/reference/errors.md#rate_limited), [`internal_error`](https://ownrecord.io/docs/reference/errors.md#internal_error).
