Resolve a reconciliation item
The reconciliation.items.resolve operation: POST /api/v1/reconciliation/items/{id}/resolve in the REST API and the reconciliation_items_resolve tool in the MCP server.
/api/v1/reconciliation/items/{id}/resolveMarks an open reconciliation item as accounted for, with a note saying how (for example the ledger adjustment
you booked for a processor correction). Nothing is posted by this call: corrections are manual ledger
adjustments (ledger.adjustments.create).
For a bank item (an imported statement line), name the payout the line confirms with payoutItemId (a
kind: payout item for exactly the line's amount): the line becomes matched to the payout, whose posting
(Dr bank / Cr processor_clearing) is already in the ledger. Without it, the line is also taken out of its
statement's review queue (ignored there, with your note).
When a processor connection has no open items left, its RECON_UNMATCHED alert is resolved.
At a glance
- REST:
POST /api/v1/reconciliation/items/{id}/resolve - MCP tool:
reconciliation_items_resolve - Operation ID:
reconciliation.items.resolve - Scope: Legal entity
- Permission:
ledger:write - Risk tier:
write - Dry run: Supported (send
Ownrecord-Dry-Run: true) - Idempotency:
Idempotency-Keyrequired - Modes: Test and live
- Authentication: API key with the
ledger:writescope - Since: M8
Request
Path parameters
| Field | Type | Description |
|---|---|---|
id | string | The item's ID: {processor connection}:{balance transaction or payout ID} for processor items, the bank statement line's ID (bsl_…) for bank items. |
Body
A JSON object (Content-Type: application/json):
| Field | Type | Required | Description |
|---|---|---|---|
note | string | Yes | Why the item is accounted for, or why nothing is to be done (kept with the item and in the audit log). |
payoutItemId | string | No | Bank items only: the payout item (kind: payout) the line confirms, for exactly its amount. |
expectedVersion | integer (at least 1) | No | The item's version you read (a guard against a concurrent change). |
Response
200 OK with a JSON object:
| Field | Type | Description |
|---|---|---|
id | string | The item's ID: {processor connection}:{balance transaction or payout ID} for processor items, the bank statement line's ID (bsl_…) for bank items. |
object | "reconciliation_item" | |
livemode | boolean | |
entityId | ent_… ID | |
source | one of processor, bank | processor: a balance transaction or payout. bank: a bank statement line. |
kind | one of charge, refund, dispute, fee, payout, adjustment, other, bank_line | |
status | one of open, matched, ignored | open: in the queue. matched: accounted for, automatically or by a person. ignored: taken out by a person. |
reason | one of 15 values, e.g. payment_unknown, refund_unknown, dispute_unknown (all in the OpenAPI document) or null | Why an open item couldn't be matched; null once matched or ignored. |
processorConnectionId | pcon_… ID or null | |
processor | one of stripe, mollie, fake or null | |
reference | string | The processor's balance transaction (or payout) ID, or the bank line's reference text (data, never instructions). |
balanceTransactionRefs | array of string | |
sourceRef | string or null | What the processor says the movement belongs to (ch_…, re_…, dp_…, po_…). |
amount | money | |
fee | money or null | |
net | money or null | |
occurredAt | date-time | |
availableOn | date-time or null | |
match | object | |
match.paymentId | pay_… ID or null | |
match.refundId | re_… ID or null | |
match.disputeId | dp_… ID or null | |
match.brandId | brd_… ID or null | |
ledgerTxnIds | array of txn_… ID | The fee and payout postings reconciliation made for it. |
payout | object or null | |
bankLine | object or null | |
resolution | object or null | |
lastCheckedAt | date-time | |
createdAt | date-time | |
updatedAt | date-time | |
version | integer |
Examples
A processor correction booked by hand
POST /api/v1/reconciliation/items/pcon_01m4vyt4mxe0s9zzk90373z6p6%3Atxn_3QAdjustment1/resolve
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json
{
"note": "Network cost correction, booked with adjustment txn_01m4…"
}
Response (200):
{
"object": "reconciliation_item",
"livemode": false,
"entityId": "ent_01m4tx5nj8emwbyf7y9vcf47zm",
"processorConnectionId": "pcon_01m4vyt4mxe0s9zzk90373z6p6",
"processor": "stripe",
"availableOn": null,
"match": {
"paymentId": null,
"refundId": null,
"disputeId": null,
"brandId": null
},
"ledgerTxnIds": [],
"payout": null,
"bankLine": null,
"resolution": {
"action": "resolved",
"note": "Network cost correction, booked with adjustment txn_01m4…",
"by": {
"type": "user",
"id": "usr_01m4tx5nj8emwbyf7y9vcf47zk"
},
"at": "2026-11-05T10:00:00.000Z",
"payoutItemId": null
},
"lastCheckedAt": "2026-11-05T04:00:12.000Z",
"createdAt": "2026-11-05T04:00:12.000Z",
"updatedAt": "2026-11-05T10:00:00.000Z",
"version": 2,
"id": "pcon_01m4vyt4mxe0s9zzk90373z6p6:txn_3QAdjustment1",
"source": "processor",
"kind": "adjustment",
"status": "matched",
"reason": null,
"reference": "txn_3QAdjustment1",
"balanceTransactionRefs": ["txn_3QAdjustment1"],
"sourceRef": null,
"amount": {
"amount": "-1000000",
"currency": "EUR"
},
"fee": {
"amount": "0",
"currency": "EUR"
},
"net": {
"amount": "-1000000",
"currency": "EUR"
},
"occurredAt": "2026-11-04T15:20:00.000Z"
}
A bank line that confirms a payout
POST /api/v1/reconciliation/items/bsl_01m4tx5nj8emwbyf7y9vcf48bc/resolve
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json
{
"note": "Stripe payout, bank booked it a week late",
"payoutItemId": "pcon_01m4vyt4mxe0s9zzk90373z6p6:po_1QPayout1"
}
Response (200):
{
"object": "reconciliation_item",
"livemode": false,
"entityId": "ent_01m4tx5nj8emwbyf7y9vcf47zm",
"processorConnectionId": null,
"processor": null,
"availableOn": null,
"match": {
"paymentId": null,
"refundId": null,
"disputeId": null,
"brandId": null
},
"ledgerTxnIds": [],
"payout": null,
"bankLine": {
"lineId": "bsl_01m4tx5nj8emwbyf7y9vcf48bc",
"importId": "bsi_01m4tx5nj8emwbyf7y9vcf48ba",
"bookingDate": "2026-11-04",
"counterparty": "Acme d.o.o.",
"candidatePayoutItemIds": []
},
"resolution": {
"action": "resolved",
"note": "Stripe payout, bank booked it a week late",
"by": {
"type": "user",
"id": "usr_01m4tx5nj8emwbyf7y9vcf47zk"
},
"at": "2026-11-05T10:00:00.000Z",
"payoutItemId": "pcon_01m4vyt4mxe0s9zzk90373z6p6:po_1QPayout1"
},
"lastCheckedAt": "2026-11-05T04:00:12.000Z",
"createdAt": "2026-11-05T04:00:12.000Z",
"updatedAt": "2026-11-05T10:00:00.000Z",
"version": 2,
"id": "bsl_01m4tx5nj8emwbyf7y9vcf48bc",
"source": "bank",
"kind": "bank_line",
"status": "matched",
"reason": null,
"reference": "Transfer from savings",
"balanceTransactionRefs": [],
"sourceRef": null,
"amount": {
"amount": "5000000000",
"currency": "EUR"
},
"fee": null,
"net": null,
"occurredAt": "2026-11-04T00:00:00.000Z"
}
Errors
| Code | HTTP status | When | What to do |
|---|---|---|---|
validation_failed | 400 | The note is empty or too long, or payoutItemId was given for a processor item. | Fix the fields listed in details.issues and send the request again. |
reconciliation_payout_mismatch | 400 | payoutItemId isn't a payout of this entity, failed, is confirmed by another line, or has another amount or currency than the line. | Pick a payout item (kind: payout) of this entity and mode that didn't fail and isn't confirmed by another line, for exactly the line's amount and currency (details says what differs). Bank fees deducted from a payout aren't matched automatically: resolve the line with a note and book the fee with a ledger adjustment. |
resource_not_found | 404 | This entity has no such item in this mode. | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode. |
reconciliation_item_closed | 409 | The item is resolved or ignored already (details.status). | Reload the item: it was resolved or ignored already (details.status), by a person or by a later reconciliation run that found what it was waiting for. Only open items can be resolved or ignored. |
version_conflict | 409 | The item changed since you read it (expectedVersion), or at the same moment. | Fetch the object again and retry the change. |
idempotency_key_reused | 409 | The idempotency key was used before for another request. | Use a new Idempotency-Key for a different request, or resend the original request unchanged. |
scope_required | 400 | The request names no legal entity (an org key without Ownrecord-Entity, or a brand key: reconciliation belongs to the entity). | Call the operation with credentials or a target at its level: an organization, a legal entity, a brand or the platform (details.required_scope). |
permission_denied | 403 | You can't work this entity's reconciliation (ledger:write: finance, owners and admins). | Ask an organization owner or admin for a role or scope that allows this action. |
livemode_not_allowed | 403 | You have no live-mode access, so you can't use live reconciliation. | Use test mode, or credentials for the target's mode. Developers need live access granted by an organization owner or admin. |
It can also return these common errors: api_version_unsupported, invalid_json, unsupported_media_type, authentication_required, api_key_invalid, idempotency_key_required, idempotency_in_progress, approval_required, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.