API Reference
Is there an API, and what can it do? Yes: BigLedger’s REST API is served under /core2/, and this section is its reference. Every endpoint follows one shape:
/core2/{tnt|platform}/{dm|ms|cm}/<family>/<resource>/<access-endpoint>The last segment is the access endpoint, and it names the caller the route was designed for — backoffice-ep for a signed-in back-office user, login-ep for an authenticated portal login, login-entity-ep for a customer or supplier acting on their own records, etl-ep for a server-to-server access key, and public-ep where an endpoint is deliberately unauthenticated. It is a naming convention rather than the authorisation check itself, which matters more than it sounds: see the two layers. Authentication covers the credentials; the Data API reference covers headers, paging, envelopes and limits.
How much API there is
The whole surface is derived from the backend’s controllers into a route table, so the numbers here are counted, not estimated — as at the backend commit named in the front matter, regenerated on 25 September 2026. At that commit the backend serves 18,720 routes from 2,218 controllers. 18,707 are under /core2/; the other 13 are a home page, a diagnostics endpoint, a short-link redirect and eight routes for the embedded web-chat widget.
By access endpoint: 1,191 routes are etl-ep — the server-to-server surface an integration uses (see the integration path) — and 9,491 are backoffice-ep, the surface the platform’s own applets call with a signed-in user’s token. The remaining 8,038 serve logins, public storefront pages, platform operation, and — 5,770 of them — carry no access segment at all. The figures move every time the backend adds a controller: on 15 September the same count read 18,630 routes and 1,116 etl-ep.
Those tallies classify a route by the segment in its path, which is a naming convention and not the authorisation check — see below before reading any of them as a limit on what your credential can reach.
Two references, and which one answers your question
There are two reference layers under this section, built by different methods, and they answer different questions. Neither is a draft of the other.
| Module references — generated | ERP Core API — hand-written | |
|---|---|---|
| What it gives you | Every route, per controller: method, path, handler, and the file and line it is declared at | Six resources in depth: request and response bodies, what each field means, what is required, the document lifecycle, and what a failure looks like |
| How it was made | Rendered from a route table exported from the backend’s 2,216 Spring controllers at a named commit | Written from the backend, then verified by calling the live API — all 117 documented paths called, create, update and delete exercised on every resource, the test records removed afterwards |
| How complete it is | Complete over one slice: routes whose path carries an etl-ep segment, plus read-only public-ep routes, for the controllers each page names | Six resources only, chosen because an integration needs them first. Deliberately not complete |
| How it goes wrong | It cannot drift — a regeneration fails if a page and the backend disagree — but it describes no behaviour at all | It is prose, so it goes stale when the product moves; it carries the commit and the date it was verified at |
Read the generated page to find out whether a route exists, what it is called, and what else the same controller serves. Read the ERP Core page when you are about to write the call and need the body, the required fields and the failure modes. Then Data API for the things common to every call — hosts, headers, paging, the response envelope, limits.
The two layers do not partition the API
A route missing from the generated pages is not a route your access key is refused. The access segment in a path is a naming convention; the backend does not use it to decide which credential is accepted, and falls back in both directions:
- an
etl-ephandler that finds no usableAccessId/AccessKeypair tries the bearer token instead, so anetl-eproute also serves a signed-in user; - a request that carries no
Authorizationheader at all falls through to the access-key interceptor, so a route with noetl-epsegment can still be called with an access key.
What decides the outcome is the permission the handler checks, which each ERP Core page lists per operation.
This is why the two layers do not line up. Until 17 September 2026 the sharpest case was chart of
accounts, ledgers and cashbooks: their controllers declared no etl-ep route at all, so they appeared on
no generated page, and they were reachable with an access key only on their plain paths. The backend now
declares an etl-ep set for all three, and the Accounting API
lists them. Their ERP Core pages still carry the bodies, the required fields and the failure modes.
What a call sets off after it answers
For example, a group whose online store pushes each paid web order into BigLedger as a sales invoice
creates the document on .../gen-doc/internal-sales-invoices/etl-ep and then moves it to FINAL with
PUT .../gen-doc/internal-sales-invoices/etl-ep/update-posting-status/{guid} — the same two steps the
purchase invoice page walks through. The second call is
the one that matters, and most of what it does happens after it has answered you:
- Before anything changes, the document is validated for posting. If validation fails, nothing is updated and nothing is queued — the document keeps the status it had.
- In one database transaction the status becomes
FINALand the company and branch running numbers are assigned. At the same moment the document is marked to skip e-invoicing if your request said so, or if its branch or its customer or supplier is set to skip. A request cannot switch that off, so when a finalised document never reaches LHDN, look at the branch and the entity before your payload — see the e-invoice module. - Only then is the document handed to the primary job processor on the tenant’s queue, which is
started asynchronously. Your
200comes back without waiting for it. That processor fans out to the secondary processors that write the journal (financial accounting), the stock movement (inventory), membership points and the cashbook — and once the stock movement exists, the cost is computed and written back onto the document line.
So a 200 on the posting-status call means the document is final and numbered, not the journal exists.
If you read the document back in the same second, its cost fields may still be empty; read it again once
the queue has run. Two things must exist before you can do any of this: an access key, and a login
subject behind it that holds the update permission for that document type — a key carries no scope of its
own and acts as exactly the user it belongs to (Authentication). Whether to
poll for the result or wait for a push is the question Webhooks answers, including
the guarantees it does not offer.
Families, and which have a reference page
The family is the first segment after /core2/{tnt|platform}/{dm|ms|cm}/. The twenty largest, with what is documented today:
| Family | Routes | Reference page today |
|---|---|---|
erp — documents, entities, items, finance, e-invoice | 8,753 | The ERP Core API (chart of accounts, ledgers, cashbooks, suppliers, purchase invoices and payment vouchers, with request and response bodies verified against production); the eight module references below (its etl-ep and read-only public-ep surface, one table per controller); the E-Invoice API (16 back-office routes with request and response bodies); the Data API reference (19 etl-ep routes for generic documents, financial items and entities) |
alg — the contact centre and bots | 1,077 | Unified Contact Center API (357 routes, 35 of 82 controllers) |
crm | 817 | — |
cms — content and storefront pages | 785 | — |
dm — tenant-level records outside a module (branches, companies, labels) | 456 | — |
wf — workflow and issues | 442 | — |
pgw — payment gateways | 293 | Integrations API (payment providers and payment transactions, 10 etl-ep routes) |
sfa — sales force automation | 283 | — |
fi — finance outside erp | 272 | — |
mrp — manufacturing | 271 | — |
applet — applet registry and links | 267 | — |
ecomsync — marketplace synchronisation | 256 | Integrations API — the connectors themselves serve backoffice-ep only and the page lists them as not exposed |
del — delivery | 244 | — |
dgmkt — digital marketing | 232 | — |
hr | 203 | — |
pyrll — payroll | 202 | — |
identity — login, access keys, subjects | 206 | Authentication (login and access-key issue) |
inv — inventory | 193 | Inventory API (2 etl-ep routes; the rest is backoffice-ep) |
iam — permissions and roles | 189 | — |
tpy — third-party services (e-signing, e-KYC, credit checks) | 183 | Integrations API — backoffice-ep only, listed as not exposed |
A further 190 families — most of them platform-side and under fifty routes each — share the remaining 3,096. The full list, with a count per family, is in the same route table, which is not published; ask your BigLedger contact about a family this page does not name. Two of them have a page. Audit Trail API covers the six routes of applet-audit-trail, together with the six erp routes that serve the same record from a tenant’s own database, which is the surface behind the Tenant Admin applet’s Audit Trail screen. FPX E-Mandate API covers the eight FPX routes of paygw, the only way to create a direct-debit mandate, because no installable applet has a screen for it.
Module references
Eight module references and the error-code index, every one generated from the route table so that a path can only appear on a page if the backend serves it. Each page lists the etl-ep (server-to-server access key) routes and the read-only public-ep routes of its controllers, one table per controller, the {docType} slugs it covers where the generic-document controller is involved, and closes with what the family serves on backoffice-ep only and how many of its routes carry no access segment at all. Neither of those closing lists is a statement about what an access key can reach — see the two layers. The pages carry no behaviour prose: what a route does is the Data API reference’s and the integration notes by module’ territory.
| Page | Routes | Controllers | Covers |
|---|---|---|---|
| Accounting | 214 | 30 | journals, subledgers, the chart of accounts, ledgers and cashbooks, tax transactions and filings, credit limits and terms, companies, branches and locations, payment and receipt vouchers |
| E-Invoice | 31 | 9 | documents submitted to LHDN, submission queues, LHDN integration, e-mail reports, PEPPOL |
| Sales | 171 | 20 | entities, the generic-document controller with 42 sales document slugs, pricing, commission schemes |
| Inventory | 249 | 20 | the item master, inventory items and transactions, stock takes, labels, warehouse item labels, attributes, 11 stock document slugs |
| Purchasing | 150 | 16 | entities and the generic-document controller with 40 purchase document slugs |
| Reports | 8 | 2 | two sales reports; the 84 bi-reports routes and the financial reports are backoffice-ep only. Three more are documented by hand — see below. |
| Batch and Import | 130 | 30 | CSV import files for customers, suppliers, employees, price books and nine document types; the JSON import queue |
| Integrations | 36 | 4 | marketplace item listings and fulfilments, payment gateways; the connector and third-party families are backoffice-ep only |
| Error Codes | 410 codes | — | every code value the API can return, with the 11 HTTP statuses the source declares |
Not on the generated pages: Applet Reports API hand-documents three more routes — Sales Report By Document, Historical Stock Balance and Profit Loss Report. They merged to the backend on 2026-09-30, after the 25 September count above, and are rows in the route table at the pinned commit; the generated Reports page lists a fixed set of report controllers that does not include them, so this is the only place they are written down.
The integration surface — etl-ep plus read-only public-ep — is 1,425 routes at the 25 September commit (1,191 plus 234). When the eight pages were first generated on 15 September they covered 754 of the 1,349 then on it, and the Unified Contact Center pages a further 57. No page yet: the other contact-centre routes (Q-0396), cms, crm, the claims sub-family under erp, and the storefront reads (reviews, item views, catalogue search) — and two sets the backend added after that measurement: 48 etl-ep routes for e-invoices received from LHDN (the eight MyEInvoiceFromIrb… and XtnEInvoiceDocValidateFromIrbQueue controllers, six routes each) and six read routes for the webhook queue and its history (generic-webhook-queues, generic-webhook-histories). They are rows in the route table; no page here lists them yet, so open an issue before building on them. Anonymous write routes on public-ep are deliberately not documented anywhere.
What a route does, module by module, is in the hand-written integration notes by module and the membership endpoints.
When it goes wrong: symptom first
The full list of codes is the error index; these are the answers that send integrators the wrong way, because the status does not mean what it usually means.
| What you see | What it means here | What to do |
|---|---|---|
417 with SERVER_GENERAL_UNKNOWN_ERROR on a path you copied from a page | The API answers every unhandled error with 417 — and a path that matches no route is one of them. There is no 404 for a missing route. | Check the path against the module reference: dm against ms, singular against plural, the access segment. Then run the probe below. |
400 with CLIENT_VALIDATION_GENERAL on a path you expected to be new | Either the verb is wrong for that path, or your last segment landed on a {guid} variable and failed to parse as one. A 400 does not prove the route you meant exists. | Check the verb, then probe with a sub-path no variable can absorb. |
404 with CLIENT_VALIDATION_GUID_DOES_NOT_EXIST when finalising a document you can read | The posting-status handler answers a refused update permission the same way as a GUID that does not exist, or a {docType} slug it does not know. | Confirm the GUID and the slug, then check the key’s login subject holds the update permission for that document type. |
429 with CLIENT_OVERLOAD_DATABASE | The database ran out of connection slots. It is not a rate limit, and nothing retries for you. | Back off and retry on your side; do not open more parallel calls. |
200 on finalise, but no journal and empty cost fields | The processor chain runs after the response. | Read the document back later rather than immediately. |
How you know a route exists before you build on it
Send the request without credentials, to a path with a sub-path that no {guid} variable can swallow —
.../etl-ep/query rather than .../etl-ep/something. Routing is decided before authentication, so the
answer tells you about the path, not your key: 417 means no route, anything else means the path
resolved. Run a control alongside it — the same call to a resource you know does not exist should answer
417. Without the control a 400 looks like success when it is only a failed GUID parse, which is exactly
how a route that did not exist was once recorded as verified. This was established unauthenticated; an
authenticated call to a missing route should behave the same way but has not been tested.
What is verified
The nine module references were unpublished on 6 September 2026 because they described endpoints that do not exist; the pages at those URLs today are the generated ones above. A cached copy of an old page is not a reference for anything.
Read next
The Data API reference — hosts, headers, paging, the response envelope and limits, which every call on every page above shares.
- Getting started with integration — nothing to moving real data in seven steps
- Webhooks — what BigLedger pushes to you, and the guarantees it does not offer