LedgerBee Developer
  • Getting started
  • Conventions
  • Products
  • Configuration
  • API Reference
Subscriptions
Payment flowCard paymentsProducts & PricingProduct Entitlements
Billing documents
Accounting
    Journal entriesSettlementsBudgets
WebhooksPartner-Hosted Checkout
Customer Portal
Accounting

Settlements

An open entry is a document or payment that has not been fully settled. Settling links entries that cancel each other out and closes them, so each stops counting toward what the counterparty owes.

Three calls do the whole job: list the open entries, preview what settling them would do, then settle the identical body. Route paths here are relative to the API mount, so the first is https://api.ledgerbee.com/api/v1/open-entries in full.

Name exactly one counterparty

Every call takes either customerId or supplierId, never both and never neither. An entry belongs to one side of the books, so a request carrying both is refused rather than answered about whichever id was read first.

Read the open entries

GET /v1/open-entries?customerId={id}, scope journal-entries-read — see the API Reference.

TerminalCode
curl 'https://api.ledgerbee.com/api/v1/open-entries?customerId=0197A943-2325-7829-B835-B6C71A293065' \ -H 'x-api-key: <your-key>'
Code
{ "baseCurrency": "DKK", "asOf": "2026-09-30", "entries": [ { "kind": "ANCHOR", "id": "0197A943-2325-7829-B835-B6C71A293070", "type": "INVOICE", "reference": "INV-1043", "date": "2026-08-14", "currency": "DKK", "originalAmount": 1250.0, "remainingAmount": 850.0, "version": "2026-08-14T09:12:44.000Z", "dueDate": "2026-09-13", "daysOverdue": 17 } ], "totals": { "perCurrency": [{ "currency": "DKK", "remainingTotal": 850.0 }], "baseCurrencyOutstanding": 850.0 } }

Three fields carry into the settle call: kind, id and version. remainingAmount is how much of the entry is still open, and the ceiling on what you may apply to it.

asOf answers as of the end of a given day: ?asOf=2026-08-31 reports the entries as they stood then, and measures daysOverdue against that date.

Read totals.baseCurrencyOutstanding for the balance, and never sum the rows. The rows are the individual open documents; the total also includes the counterparty's opening balance, which no row carries.

Preview the settlement

POST /v1/settlements/preview, scope journal-entries-write — see the API Reference. Nothing is posted, and the response is a 200.

The preview takes the same body as the settle call, so you send one payload twice. Nothing has to be rebuilt between them, and nothing can differ between what you were shown and what posts.

TerminalCode
curl -X POST 'https://api.ledgerbee.com/api/v1/settlements/preview' \ -H 'x-api-key: <your-key>' \ -H 'Content-Type: application/json' \ -d '{ "customerId": "0197A943-2325-7829-B835-B6C71A293065", "postingDate": "2026-09-01", "entries": [ { "kind": "ANCHOR", "id": "0197A943-2325-7829-B835-B6C71A293070", "amountToApply": 850.0, "version": "2026-08-14T09:12:44.000Z" }, { "kind": "PAYMENT", "id": "0197A943-2325-7829-B835-B6C71A293088", "amountToApply": 850.0, "version": "2026-08-30T11:04:22.000Z" } ] }'

amountToApply is a magnitude — always positive, and at most that entry's remainingAmount. Which side of the settlement an entry lands on follows from what the entry is, so there is no sign to state.

Send the absolute value. A credit note and a payment are reported with a negative remainingAmount, and passing that figure straight back is refused with SETTLEMENT_AMOUNT_TO_APPLY_EXCEEDS_REMAINING. Take Math.abs(remainingAmount). The response signs the amounts for you: an entry being consumed comes back negative, the one consuming it positive.

Echo each entry's version. It refuses the settlement if the entry was edited or reversed between reading the list and settling, instead of applying against a document that has since changed. It does not move when another settlement applies to the same entry, so an unchanged version is not a promise that as much is still open — applying more than remains is refused on its own, measured at the moment of the write.

Read blockers before settling. It carries the codes that would refuse the settlement, and an empty list is what says the settle call will go through. warnings are worth checking but do not stop it.

The difference

Settled entries rarely cancel to zero in the company's base currency. The preview reports what is left over in difference, and the settle call posts it.

difference.kindWhat it meansWhere it posts
BALANCEDThe entries cancel.Nothing posts.
FXThey cancel in their own currency but not in the company's, because they were booked at different rates.The exchange gain/loss account.
WRITEOFFA real difference in value.The debtor-loss account on the customer side, the creditor-loss account on the supplier side.

difference.account names the account, and postings lists every line that would be written.

An FX difference on entries booked at rates far apart is usually a mis-booked rate rather than a real exchange difference. The preview says so in warnings and names the currencies in rateGap, with the lowest and highest rate any selected entry in that currency was booked at.

Settle

POST /v1/settlements, scope journal-entries-write — see the API Reference. Send the body you previewed.

Code
{ "id": "0197A943-2325-7829-B835-B6C71A2930A1", "differenceJournalEntryId": "0197A943-2325-7829-B835-B6C71A2930A2", "vatReclassJournalEntryId": null }

Each settled entry now shows a smaller remainingAmount on the open-entries listing, or leaves it entirely once fully applied.

Send idempotencyKey to make a retry safe: the same key with the same body returns the settlement already recorded instead of posting a second one.

Read the settlements back

GET /v1/settlements?customerId={id} lists every settlement recorded against one counterparty, newest first; GET /v1/settlements/{id} returns one in full, with the entries it linked and how much of each it applied. Both take scope journal-entries-read.

Reversed settlements are included in the list rather than hidden, because they are part of the trail. Check reversed before treating one as still standing: a reversed settlement contributes nothing, and the entries it linked are open again and back on the open-entries listing.

An entry's appliedAmount can be smaller than its originalAmount. A payment is consumed across as many settlements as it takes, and whatever is left stays open.

Errors

ConditionWhat you observeFix
The request names both a customer and a supplier, or neither400 SETTLEMENT_COUNTERPARTY_REQUIREDName exactly one
The counterparty id is not one of yours404 CUSTOMER_NOT_FOUND or 404 SUPPLIER_NOT_FOUNDResolve ids from GET /v1/customers or GET /v1/suppliers
Fewer than two entries400 SETTLEMENT_INSUFFICIENT_SELECTIONSA settlement links something owed to something that pays it
An entry id is not open, or not this counterparty's400 SETTLEMENT_SELECTION_NOT_FOUNDRe-read GET /v1/open-entries and use the ids it reports
An entry was settled in full since you read the list400 SETTLEMENT_SELECTION_ALREADY_FULLY_CONSUMEDRe-read the listing; the entry is gone from it
amountToApply exceeds the entry's remainingAmount400 SETTLEMENT_AMOUNT_TO_APPLY_EXCEEDS_REMAININGApply at most remainingAmount
amountToApply is zero400 SETTLEMENT_AMOUNT_TO_APPLY_ZEROApply a positive amount, or drop the entry from the request
The entry changed since you read its version400 SETTLEMENT_OPTIMISTIC_LOCK_VIOLATIONRe-read the listing and settle against the current entry
The document behind an entry has been reversed400 SETTLEMENT_SELECTION_DOCUMENT_REVERSEDThe document is voided and owes nothing
The applied amounts pay more than is owed400 SETTLEMENT_OVERPAYMENT_REQUIRES_PARTIAL_CONSUMEApply the amount actually owed and leave the rest of the payment open
postingDate falls in a closed period or year400, one of ACCOUNTING_PERIOD_CLOSED_FOR_POSTING, ACCOUNTING_YEAR_CLOSED, ACCOUNTING_PERIOD_NOT_CONFIGURED, ACCOUNTING_YEAR_NOT_RESOLVED, ACCOUNTING_YEAR_PENDING_RECLOSEPost to a date in an open period
The difference needs an account your company has not configured400, one of EXCHANGE_GAIN_LOSS_ACCOUNT_NOT_CONFIGURED, DEBTOR_LOSS_ACCOUNT_NOT_CONFIGURED, CREDITOR_LOSS_ACCOUNT_NOT_CONFIGUREDAssign the account in the gated app under Settings, then retry
No debtors/creditors control account covers the entries400 SETTLEMENT_CONTROL_ACCOUNT_NOT_CONFIGUREDAssign the control account to the customer or supplier group
The same idempotencyKey was reused with a different body400 SETTLEMENT_IDEMPOTENCY_KEY_CONFLICTUse a new key for a different settlement

Reference

Field-level documentation for every endpoint and response shape is in the API Reference.

Last modified on September 13, 2026
Journal entriesBudgets
On this page
  • Name exactly one counterparty
  • Read the open entries
  • Preview the settlement
  • The difference
  • Settle
  • Read the settlements back
  • Errors
  • Reference
JSON
JSON