LedgerBee Developer
  • Getting started
  • Conventions
  • Products
  • Advisor firms
  • Configuration
  • API Reference
Subscriptions
Payment flowCard paymentsProducts & PricingDiscountsProduct Entitlements
Billing documents
Accounting
    Journal entriesSettlementsBank transactionsBudgets
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, "settledWithRemainder": false } ], "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.

Paid documents with an amount left

An invoice or credit note can be recorded as paid while part of it is still left on it, for example 0.11 left after a payment that fell a few øre short. The listing includes such a document with settledWithRemainder: true, and its remainingAmount and baseRemainingAmount are only the amount left. That amount is part of the counterparty's balance, so totals.perCurrency counts it.

Such an entry settles only together with entries that cancel it exactly in the base currency, so the settlement posts nothing. Apply all of what is left, as Math.abs(remainingAmount) like any other entry; any other selection that includes it is refused with SETTLEMENT_SETTLED_DOCUMENT_REQUIRES_BALANCED_MATCH (see Errors).

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. Also entries in the company's currency and one foreign currency whose difference is within 5% of the foreign entries' total at the posting date's exchange rate. Also a payment in a third currency, such as a USD invoice paid in EUR: no entry is in the company's currency, every entry is settled in full, and the two currencies are worth within 5% of each other at the posting date's exchange rate.The exchange gain/loss account.
WRITEOFFA real difference in value. A payment in a third currency that is more than 5% short or over, or that leaves an entry partly open, is one.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.

A difference is posted only while it is a real exchange difference. An entry whose base-currency amount is its own amount at the rate it was booked at has nothing to check. When an entry carries more than 10 in the base currency beyond that, every exchange rate the extra amount rests on must be a market rate: the entry's own rate, the rate each earlier payment on it was converted at, the rate of a payment's open rest, and, for an amount an invoice and a credit note linked to it hand to each other, the own rate of each linked document the amount comes from and the rates of the payments on it. Each is checked against the market rates LedgerBee stores for the day the rate belongs to, the rate date the entry was booked with or else its document date, or the nearest stored day up to three days away. A rate within 2% of them for EUR, or 10% for any other currency, is a market rate, and the difference is then booked however large it is, as a currency that really moved produces. A rate further off, such as a payment converted at a rate stored per 100, is a wrong figure and is refused (see Errors). Where no market rate is stored near a rate's day, or the amount comes from a linked document in another currency, the difference is posted only while the amount resting on that rate is within 10 in the base currency or 5% of the entry's base-currency amount. Settling an entry against entries that cancel it in the base currency posts nothing and is always accepted.

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
An entry is a document already recorded as paid that still has an amount left, and the other entries do not cancel that amount exactly400 SETTLEMENT_SETTLED_DOCUMENT_REQUIRES_BALANCED_MATCHApply the whole amount left, against entries that net it to zero in the base currency (they may be in another currency than the document), with no account to book a difference to
Such a match has a postingDate on or before the day the document was recorded as paid400 SETTLEMENT_SETTLED_DOCUMENT_POSTING_DATE_NOT_AFTER_STAMPPost the match on a later date
The settlement would book a difference, an entry it closes carries more than 10 in the base currency beyond its own amount at its booked rate, and a rate that amount rests on (the entry's own, an earlier payment's, a payment's open rest, a linked invoice's or credit note's) is outside the band around the market rates stored for that rate's day. Also a payment in a third currency (an FX difference with no entry in the base currency) where an entry's own booked rate is outside that band: the difference would be that rate's error, not an exchange difference400 SETTLEMENT_RESIDUAL_ENTRY_RATE_IMPLAUSIBLE. details.reference names the entry whose rate is wrong; details.rate is the rate its figures imply and details.lowestStoredRate / details.highestStoredRate the stored market rates for details.rateDate, as strings in details.currency per one details.foreignCurrency; details.selectionIds lists every refused entryCorrect the entry's amounts, or settle it against entries that cancel it in the base currency, so nothing is booked
The settlement would book a difference, an entry it closes carries a base-currency amount its own currency does not account for at its booked rate, above 10 in the base currency and above 5% of the entry's base-currency amount, or an earlier payment on it is that far off its own amount, and that amount cannot be checked against a market rate: none is stored near the day a rate it rests on belongs to, or a linked document in another currency hands it over400 SETTLEMENT_RESIDUAL_CARRIES_UNEXPLAINED_BASE_GAP. details.reference names the first such entry; details.baseGap and details.baseAmount are its figures, or the earlier payment's when that payment is what is off, as unsigned two-decimal strings in details.currency, the base currency; details.selectionIds lists every refused entrySettle the entry against entries that cancel it in the base currency, so nothing is booked, or correct its base-currency amounts
The settlement would book a difference, and an entry it closes is in the base currency but its base-currency amount differs from its own amount400 SETTLEMENT_RESIDUAL_CARRIES_BASE_GAP_ON_BASE_CURRENCY_DOCUMENT, with the same details fieldsSettle the entry against entries that cancel it, so nothing is booked, or correct its amounts
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 LEDGER_ACCOUNTING_PERIOD_CLOSED_FOR_POSTING, LEDGER_ACCOUNTING_YEAR_CLOSED, LEDGER_ACCOUNTING_PERIOD_NOT_CONFIGURED, LEDGER_ACCOUNTING_YEAR_NOT_RESOLVED, LEDGER_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 November 6, 2026
Journal entriesBank transactions
On this page
  • Name exactly one counterparty
  • Read the open entries
    • Paid documents with an amount left
  • Preview the settlement
  • The difference
  • Settle
  • Read the settlements back
  • Errors
  • Reference
JSON
JSON