Versioning & Deprecation
The Public API is versioned in the URL path. All endpoints live under the base
URL https://api.ledgerbee.com/api, followed by the version segment. Most of
the surface is /v1 (e.g. GET /api/v1/company); a few journal-entry
endpoints have /v2 successors.
Stability within a version
Within a major version the API is additive-only:
- New fields may appear in responses at any time. Parse leniently and ignore fields you don't recognize.
- Existing fields are never renamed, removed, or retyped.
Breaking changes ship only as a new version under a new path segment, leaving the old version running.
Deprecation strategy
Nothing is removed from the API without warning. Retiring an endpoint or a field follows a fixed lifecycle:
- Announce. The operation or field is flagged
deprecatedin the API Reference, and a deprecated endpoint starts sending aDeprecationheader on every response. A successor is always named — a/v2endpoint, a sibling resource, or a renamed field. - Schedule a sunset. Once an endpoint has a shutdown date, every response
also carries a
Sunsetheader (the three headers are shown below). That date is the latest the endpoint can be removed: while anyone is still calling it, it keeps working unchanged until then. An endpoint whose callers have all migrated may be retired earlier. The date bounds how long you have to migrate; it does not keep an endpoint nobody calls reachable. - Remove. The endpoint is deleted and moves to Removed endpoints. Its successor has been live throughout.
Field renames skip the sunset step — a field cannot carry a Sunset header. The
old name stays accepted on input and returned in responses with the same value
until the next major version; its only signal is the deprecated flag in the
API Reference.
Treat any Deprecation header as a migration task: alert on it and move to the
successor before the Sunset date. Within a version, nothing is renamed,
removed, or retyped outside this lifecycle.
Current /v2 endpoints
| v2 endpoint | Replaces |
|---|---|
POST /api/v2/journal-entries | POST /api/v1/journal-entries, removed |
POST /api/v2/journal-entries/batch | POST /api/v1/journal-batches, removed |
POST / GET /api/v2/draft-journal-entries (+ /{id}, /{id}/book) | the /api/v1/draft-journal-entries equivalents |
POST /api/v2/draft-journal-batches | POST /api/v1/draft-journal-batches |
The v2 journal-entry endpoints auto-append the AR/AP leg on the customer/supplier group default account, eliminating the most common booking error. Full request/response shapes are in the API Reference.
How deprecation is signaled
Deprecation is signaled in two machine-readable ways:
- In the API Reference — deprecated operations carry the OpenAPI
deprecatedflag and a migration note in their description. - On the wire — endpoints with a scheduled shutdown tag every response with three headers:
| Header | Example value | Meaning |
|---|---|---|
Deprecation | @1778371200 | The date the endpoint was deprecated, in Unix seconds (RFC 9745). |
Sunset | Thu, 31 Dec 2026 00:00:00 GMT | HTTP-date (RFC 7231 IMF-fixdate) after which the endpoint may stop working (RFC 8594). |
Link | </api/v2/draft-journal-entries>; rel="successor-version" | Where to migrate. |
Deprecated endpoints function normally until their sunset date; the headers
are informational only. Log a warning whenever your client sees a
Deprecation header so migrations surface before the sunset.
Currently deprecated endpoints
Every endpoint below shares the same sunset, 2026-12-31, and works unchanged
until then.
| Endpoint | Migrate to | Sunset |
|---|---|---|
/api/v1/draft-journal-entries — create, list, get, book, delete | /api/v2/draft-journal-entries | 2026-12-31 |
POST /api/v1/draft-journal-batches | POST /api/v2/draft-journal-batches | 2026-12-31 |
/api/v1/vendors — list, groups, contacts, departments, create | /api/v1/suppliers — identical shapes with supplier-named fields and SUPPLIER_* error codes | 2026-12-31 |
A page past the first on a list with more than 10,000 matching rows (paging smaller lists by page is deprecated but stays) | cursor / nextCursor — see Pagination | 2026-12-31 |
Each response from the v1 drafts and /api/v1/vendors carries a Deprecation
header with the date the endpoint was deprecated (@1778371200 and
@1781481600), a Sunset header of Thu, 31 Dec 2026 00:00:00 GMT, and a
Link to the successor. Page-number paging is signaled only on a request that
sends page greater than 1 without cursor: Deprecation: @1791417600 and a
Link to the pagination guide, plus Sunset when the list has more than 10,000
matching rows. The
/api/v1/vendors alias additionally keeps serving the legacy vendorId-style
field names and VENDOR_* error codes until the sunset; its Link successor is
/api/v1/suppliers.
Removed endpoints
| Endpoint | Use instead |
|---|---|
POST /api/v1/journal-entries | POST /api/v2/journal-entries |
POST /api/v1/journal-batches | POST /api/v2/journal-entries/batch |
Both return 404. They were retired ahead of their published 2026-12-31
sunset once no integrator traffic remained on either.
The rest of /api/v1/journal-entries is unaffected and carries no deprecation:
PATCH /api/v1/journal-entries/{id}, POST /api/v1/journal-entries/reverse,
POST /api/v1/journal-entries/batch-reverse, GET /api/v1/journal-entries,
GET /api/v1/journal-entries/{id}, and
GET /api/v1/journal-entries/{id}/attachments. Entries created through
/api/v2/journal-entries are read back through those same v1 endpoints.
Deprecated fields
When a field is renamed, the old name keeps working — accepted on input and still
returned in responses with the same value — until it is removed in a future major
version. A field cannot carry a Sunset header, so the only signal is the
OpenAPI deprecated flag on the property in the API Reference.
| Field / value | Use instead | Where |
|---|---|---|
vendorId | supplierId | journal-entry and draft-journal-entry create bodies, responses, the list vendorId filter (v1 + v2), and journal-batch entries |
amount, currency, exchangeRate, exchangeRateDate, list minAmount / maxAmount | the names under Field names on a draft. amount has two successors: originalAmount on a v1 create request, baseCurrencyAmount on a read | the v1 and v2 draft-journal-entry endpoints and draft batches. Not the booking response, which is the posted entry described in the exchangeRate, exchangeRateDate on a posted entry row below |
currency, exchangeRate on a create request | originalCurrency, originalCurrencyToBaseCurrencyExchangeRate, described under Posting an entry | POST /api/v2/journal-entries and POST /api/v2/journal-entries/batch request bodies |
exchangeRate, exchangeRateDate on a posted entry | originalCurrencyToBaseCurrencyExchangeRate, originalCurrencyToBaseCurrencyExchangeRateDate, described under The three currency layers | the posted entry returned by POST /api/v2/journal-entries and its batch, GET /api/v1/journal-entries and GET /api/v1/journal-entries/{id}, and booking a draft (POST /api/v1/draft-journal-entries/{id}/book, POST /api/v2/draft-journal-entries/{id}/book). Its amount and currency are the figure and currency of the account accountId names, which can differ from originalAmount and originalCurrency, and are not deprecated |
splitEntries on a read | lines | v1 and v2 draft-journal-entry responses. It reports the lines in its own shape rather than the same value, described under Drafts. The v1 create takes splitEntries, which is not deprecated there |
entryType: "invoice" | entryType: "supplier_invoice" | journal-batch entries |
entryType: "payment" | entryType: "supplier_payment" | journal-batch entries |
Both supplierId and vendorId are accepted on input (supplierId wins), and
every response that carries a supplier returns both with the same value; a
response with no supplier carries neither. Sending supplierId and
vendorId with different values returns 400, with two exceptions: the draft
list filters on supplierId, and the v1 draft create accepts both without
comparing them and stores no supplier on the draft. customerId is unchanged.
The deprecated entryType values still create supplier invoices and payments; the
explicit supplier_invoice / supplier_payment values do the same thing under
a clearer name.
API key expiration header
When you authenticate with an API key that has an expiry date, every successful response carries
Code
Monitor it and rotate the key before it lapses. See API Keys for creating and rotating keys.