External Reconciliations
An external reconciliation is a dated record that an outside figure was compared with the ledger. The outside figure is one of two subjects:
- a bank transaction (
relation_type=PAYMT), matched against one or more journal entry lines; - a document reconciliation line — a line extracted from a supplier statement, an annual report or any other document that carries reconciliation lines, see Documents — addressed by its document:
relation_type= the document’s type (e.g.AP_STATEMENT,ANNUAL_REPORT),relation_id= the document id,relation_line_id= the line id; matched against journal entry lines or an account balance. The line’scategorydecides the sign and the target accounts.
Unlike internal reconciliations (which match journal entry lines against each other), external reconciliations link accounting entries to their external counterparts.
Each reconciliation has entries of four kinds:
| Kind | Points at | Amount |
|---|---|---|
EXTERNAL |
A bank transaction (relation_type = PAYMT, relation_id) or a document reconciliation line (relation_type = the document’s type, relation_id = document id, relation_line_id = line id) |
The signed bank transaction amount, or the document line’s amount in ledger sign (target.ledger_sign × amount) |
LEDGER_LINE |
A journal entry line (relation_type = JE, relation_id + relation_line_id) |
debit − credit of the line, in the reconciliation currency |
LEDGER_BALANCE |
A general ledger account (relation_type = GENERAL_LEDGER_ACCOUNT, relation_id = the account id), optionally scoped to a sub-ledger dimension through secondary_relation_type / secondary_relation_id, as of as_of_date |
The account balance at that date, computed by the server |
RECONCILING_ITEM |
Nothing — a free-text description |
A signed movement that explains part of the difference |
All amounts are in ledger sign: debit positive, credit negative. The server computes three snapshot amounts from the entries and stores them on the reconciliation: expected_amount (sum of EXTERNAL entries), ledger_amount (sum of LEDGER_LINE or LEDGER_BALANCE entries) and difference (expected_amount − ledger_amount). For bank transactions the difference must be zero. For document reconciliation lines the difference must equal the sum of the RECONCILING_ITEM entries, so every record is fully explained when it is made.
A document reconciliation line exposes a derived status (OPEN, RECONCILED, EXPLAINED, DRIFTED) that re-runs the comparison against the current ledger on every read; see Reconciliation Line Status.
Endpoints
List External Reconciliations
GET /api/v2/external-reconciliations
Retrieves a paginated list of external reconciliations.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| client_account_id | integer | Yes | ID of the client account |
| from_date | date | No | Filter from this date |
| to_date | date | No | Filter to this date |
| relation_type | string | No | Filter by entry relation type: PAYMT, JE, GENERAL_LEDGER_ACCOUNT, or a document type (e.g. AP_STATEMENT, ANNUAL_REPORT) |
| relation_id | integer | No | Filter by entry relation ID. A document line’s records: relation_type=<document type>&relation_id=<document id>&relation_line_id=<line id> |
| relation_line_id | integer | No | Filter by entry relation line ID |
| is_deleted | boolean | No | Include retired (soft-deleted) reconciliations. Default false |
| page | integer | No | Page number |
| per_page | integer | No | Items per page |
Response
{
"data": [
{
"id": 1,
"created_at": "2024-03-15T10:00:00Z",
"created_by_id": 7,
"client_account_id": 7,
"sequence_number": 1,
"expected_amount": "-1500.000000",
"ledger_amount": "-1500.000000",
"difference": "0.000000",
"currency_code": "NOK",
"note": null,
"is_deleted": false,
"deleted_at": null,
"deleted_by_id": null,
"entries": [
{
"id": 1,
"kind": "EXTERNAL",
"relation_type": "PAYMT",
"relation_id": 86,
"relation_line_id": null,
"secondary_relation_type": null,
"secondary_relation_id": null,
"as_of_date": null,
"description": null,
"account_code": null,
"amount": "-1500.000000",
"currency_code": "NOK",
"exchange_rate": "1.000000"
},
{
"id": 2,
"kind": "LEDGER_LINE",
"relation_type": "JE",
"relation_id": 456,
"relation_line_id": 1,
"secondary_relation_type": null,
"secondary_relation_id": null,
"as_of_date": null,
"description": null,
"account_code": null,
"amount": "-1500.000000",
"currency_code": "NOK",
"exchange_rate": "1.000000"
}
]
}
],
"meta": {
"page": 1,
"pages": 1,
"per_page": 25,
"records": 1
}
}
Create External Reconciliation
POST /api/v2/external-reconciliations
Creates a new external reconciliation with entries.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| client_account_id | integer | Yes | The client account ID |
| note | string | No | Free-text note on the reconciliation |
| entries | array | Yes | Array of reconciliation entries |
sequence_number, expected_amount, ledger_amount, difference, currency_code and reconciliation_method are computed by the server and ignored if sent.
is_draft may be sent. A draft is a prepared comparison that nobody has accepted yet: it gets no sequence_number and no reconciliation_method, and it does not count as reconciled anywhere (bank transactions and journal entry lines it references stay open). Saving it again with is_draft: false reconciles it and assigns both.
Entry Object
| Field | Type | Required | Description |
|---|---|---|---|
| kind | string | Yes | EXTERNAL, LEDGER_LINE, LEDGER_BALANCE or RECONCILING_ITEM. Must agree with relation_type |
| relation_type | string | Per kind | PAYMT or the document’s type (e.g. AP_STATEMENT, ANNUAL_REPORT) for EXTERNAL; JE for LEDGER_LINE; GENERAL_LEDGER_ACCOUNT for LEDGER_BALANCE; omitted for RECONCILING_ITEM |
| relation_id | integer | Per kind | ID of the related record: the bank transaction, document, journal entry or general ledger account. Omitted for RECONCILING_ITEM |
| relation_line_id | integer | Per kind | The line within the relation: the journal entry line id for LEDGER_LINE, the reconciliation line id for a document-line EXTERNAL; not used by other kinds |
| secondary_relation_type | string | No | LEDGER_BALANCE only: the sub-ledger dimension the balance is scoped to — businesspartner, bankacc, project, department or asset. Set together with secondary_relation_id or not at all |
| secondary_relation_id | integer | No | LEDGER_BALANCE only: ID of the dimension record (e.g. the business partner) |
| as_of_date | date | LEDGER_BALANCE |
The date the balance is taken as of. All LEDGER_BALANCE entries of one record must share it |
| description | string | RECONCILING_ITEM |
What the reconciling item is (e.g. “Invoice 1042 booked in the next period”) |
| account_code | string | — | Read-only. The account code of a LEDGER_BALANCE entry’s account, echoed by the server |
| amount | decimal | Per kind | For EXTERNAL: the signed bank transaction amount, or the document line’s amount in ledger sign (the server forces target.ledger_sign × amount whatever sign is sent). For LEDGER_LINE: debit − credit of the line (debit_fc − credit_fc when reconciling in a foreign currency); the server normalises the sign from the line itself. For LEDGER_BALANCE: ignored — the server computes the balance. For RECONCILING_ITEM: the signed amount it explains, non-zero |
| currency_code | string | Yes | ISO 4217 currency code. All entries must share one currency |
| exchange_rate | decimal | No | Exchange rate applied. If omitted, the server derives it from the matched record, or defaults to 1 when the entry currency equals the accounting currency; a 400 is returned only if neither source is available |
Requirements per kind
EXTERNALbank transaction —relation_type=PAYMT,relation_id,amountequal to the signed transaction amount,currency_code.EXTERNALdocument reconciliation line —relation_type= the document’s type,relation_id= the document id,relation_line_id= the line id,currency_code= the line’s currency,amount= ±the line’s printed amount. The server sets the sign from the line’s category: aTRADE_PAYABLEline on a supplier statement is your payable, so the entry amount is−amount; aDEPOSITline is your asset, so it is+amount. Any document type is accepted; a line without a category is refused (Reconciliation line has no category).LEDGER_LINE—relation_type=JE,relation_id,relation_line_id,amount,currency_code.LEDGER_BALANCE—relation_type=GENERAL_LEDGER_ACCOUNT,relation_id= the account id,as_of_date,currency_code; optionallysecondary_relation_type+secondary_relation_id. The server computesamountand echoesaccount_code.RECONCILING_ITEM—description, a non-zeroamount,currency_code; no relation.
Example Request
A 1 500 NOK payment out of the bank account, booked as a credit on account 1920:
{
"client_account_id": 7,
"entries": [
{
"kind": "EXTERNAL",
"relation_type": "PAYMT",
"relation_id": 86,
"amount": "-1500.00",
"currency_code": "NOK"
},
{
"kind": "LEDGER_LINE",
"relation_type": "JE",
"relation_id": 456,
"relation_line_id": 1,
"amount": "-1500.00",
"currency_code": "NOK"
}
]
}
A deposit is the mirror image: the EXTERNAL amount is positive and the 1920 line is a debit, so the LEDGER_LINE amount is positive too.
A supplier statement’s closing balance (a BALANCE reconciliation line of 12 500 NOK owed to the supplier, line id 301 on document 4711) compared with the supplier’s balance on account 2400 as of the statement date. The ledger shows 11 300 NOK because one invoice was booked in the next period; a reconciling item explains the 1 200 NOK difference:
{
"client_account_id": 7,
"entries": [
{
"kind": "EXTERNAL",
"relation_type": "AP_STATEMENT",
"relation_id": 4711,
"relation_line_id": 301,
"amount": "-12500.00",
"currency_code": "NOK"
},
{
"kind": "LEDGER_BALANCE",
"relation_type": "GENERAL_LEDGER_ACCOUNT",
"relation_id": 864,
"secondary_relation_type": "businesspartner",
"secondary_relation_id": 56,
"as_of_date": "2026-06-30",
"currency_code": "NOK"
},
{
"kind": "RECONCILING_ITEM",
"description": "Invoice 1042 dated 2026-06-28 booked in July",
"amount": "-1200.00",
"currency_code": "NOK"
}
]
}
The server stores expected_amount = -12500, ledger_amount = -11300 (the computed balance) and difference = -1200, matched by the item. The EXTERNAL amount may be sent as 12500.00; the line is TRADE_PAYABLE, so the server applies the −1 sign.
Finding candidates
Read the line with GET /api/v2/documents/{id}?with=reconciliation_lines.target (or document.reconciliation_lines.target on a voucher). target.accounts are the account codes to search and target.dimension the sub-ledger scope. For a TRANSACTIONAL line list journal entry lines with GET /api/v2/journal-entry-lines?client_account_id=…&account_code=<target codes>&relation_type=<dimension type>&relation_id=<dimension id>&from_date=…&to_date=…&include_externally_reconciled=false. For a BALANCE line read GET /api/v2/general-ledger-accounts/balances?client_account_id=…&account_code=<target codes>&as_of_date=…&relation_type=…&relation_id=… (see General Ledger Accounts); the balances are computed by the same function the sign-off stores. The line’s live record, if any, is GET /api/v2/external-reconciliations?relation_type=<document type>&relation_id=<document id>&relation_line_id=<line id>; DELETE it to retire.
Response
Returns the created external reconciliation with status 201 Created.
Update External Reconciliation
PATCH /api/v2/external-reconciliations/{id}
Content-Type: application/json
{
"note": "Explained after the bank statement arrived",
"items": [ { "description": "Card settlement posted next month", "amount": "4300.00" } ],
"is_draft": false
}
note can be changed on any record. items replaces a draft’s reconciling items. is_draft: false reconciles a draft: the items must explain the whole difference and the ledger must not have moved since the draft was prepared (409 period_reconciliation.stale_draft). See Reconciliation Periods.
Delete External Reconciliation
DELETE /api/v2/external-reconciliations/{id}
Retires an external reconciliation. The row is kept with is_deleted set to true and deleted_at stamped, so the record of what was compared survives; it no longer appears in GET unless is_deleted=true is passed. The bank transaction, document reconciliation line and journal entry lines it referenced become reconcilable again. Deleting an already retired reconciliation is a no-op.
Response
Returns 204 No Content on success.
Attributes
| Attribute | Type | Description |
|---|---|---|
| id | integer | Unique identifier (read-only) |
| created_at | datetime | Creation timestamp (read-only) |
| created_by_id | integer | ID of the user who created it (read-only) |
| client_account_id | integer | ID of the client account |
| sequence_number | integer | Sequence number for ordering (read-only); null while the record is a draft |
| is_draft | boolean | Prepared but not yet reconciled; drafts never count as reconciled (default false) |
| reconciliation_method | string | How the record was reconciled (read-only); null while it is a draft. EXTERNAL_MATCH (difference 0), EXPLAINED (difference covered by reconciling items), or one of the periodic reconciliation rules NO_ACTIVITY, ZERO_BALANCE, UNCHANGED, OPEN_ITEMS, CLEARED |
| expected_amount | decimal | Sum of EXTERNAL entries, in ledger sign (read-only) |
| ledger_amount | decimal | Sum of LEDGER_LINE or LEDGER_BALANCE entries, in ledger sign (read-only) |
| difference | decimal | expected_amount − ledger_amount; equals the sum of RECONCILING_ITEM entries (read-only) |
| currency_code | string | The entries’ shared currency (read-only) |
| note | string | Free-text note |
| is_deleted | boolean | Whether the reconciliation has been retired (read-only) |
| deleted_at | datetime | When it was retired (read-only) |
| deleted_by_id | integer | Who retired it (read-only) |
Relationships
| Relationship | Type | Description |
|---|---|---|
| entries | ExternalReconciliationEntry[] | The reconciliation entries (included by default) |
Business Rules
For every record:
- Exactly one
EXTERNALentry, and at least one ledger-side entry (LEDGER_LINEorLEDGER_BALANCE). - An entry’s
currency_codemust match the currency of the record it references, and all entries must share one currency. - A
LEDGER_LINEamount may not exceed the unreconciled remainder of its line; its sign is taken from the line. - A cancelled or draft journal entry cannot be used as a ledger line.
kindmust agree withrelation_type(see the entry table); aLEDGER_LINErequiresrelation_line_id, aRECONCILING_ITEMcarries no relation.
For bank transactions (EXTERNAL = PAYMT):
- The
EXTERNALamount must exactly match the referenced bank transaction, including sign. - A bank transaction that is already reconciled cannot be reconciled again.
- Every
LEDGER_LINEmust carry the bank-account dimension of the bank transaction’s account. - Only
LEDGER_LINEentries are allowed on the ledger side, and the difference must be zero: the ledger lines must sum to the bank transaction amount.
For document reconciliation lines (EXTERNAL with a document type as relation_type):
- A line can have at most one live record; retire the existing one first.
- The
EXTERNALamount’s absolute value must equal the line’s printed amount and its currency must equal the line’s currency. The server sets the sign from the line’s category. Any document type is accepted; a line without a category is refused. - A
TRANSACTIONALline is reconciled againstLEDGER_LINEentries only; aBALANCEline againstLEDGER_BALANCEentries only. - Every
LEDGER_LINEandLEDGER_BALANCEmust be on one of the line’starget.accounts. - Where the category names a dimension the document supplies (
target.dimension: the document’s business partner forTRADE_PAYABLE,TRADE_RECEIVABLEandLOAN, its bank account forDEPOSIT), everyLEDGER_LINEmust carry it and everyLEDGER_BALANCEmust be scoped to it, on every target account. A document without the value is not checked. - All
LEDGER_BALANCEentries of one record share the sameas_of_date. - The
RECONCILING_ITEMentries must sum todifference; each item has a description and a non-zero amount.
Periodic subjects (written by the reconciliation engine, see Reconciliation Periods): BANK_BALANCE (a bank balance imported through a bank integration; the amount is the balance, the ledger side is the bank’s accounts scoped to that bank account), VATRP addressed without a relation_line_id (the settlement document of an approved return; the VAT accounts are expected to be empty) and RECONCILIATION_PERIOD (a rule-backed record; the expected amount is the last documented balance).
- The ledger side is
LEDGER_BALANCEentries only, all as of one date on or after the subject’s date. - Reconciling items may explain part of the difference while the record is a draft; a record can only leave draft state once they explain all of it.
- While a live record references a line, the line’s
amount,currency_code,transaction_date,reconciliation_typeandcategorycannot be changed, and neither the line nor its document (or voucher) can be deleted.
Error Responses
| Status Code | Description |
|---|---|
| 400 | Invalid request (missing required fields, or a business rule above is violated) |
| 403 | Forbidden (no access to client account) |
| 404 | Reconciliation not found |