Skip to content

The Integration Surface

Every page in Integrate an External System assumes what this page says: the shape of the API your access key calls, what happens after a write has already answered, what BigLedger does not give you, and the reads that prove an integration worked. Read it once before you build, and again when a document answered 200 and nothing reached the books.

What the integration surface is

BigLedger has one REST API, served over HTTPS, returning JSON. The platform’s own screens call it too. Paths follow one shape, with more variation than the shape suggests:

/core2/{tnt|platform}/{dm|ms|cm}/<module>[/<sub-module>]/<resource>/<access-endpoint>

The segment that matters is the access endpoint. It names the kind of caller a route was built for: backoffice-ep for a staff user in the BigLedger shell, login-ep for a signed-in user acting on their own records, login-entity-ep for a customer or supplier, public-ep where a resource is meant to be open, and etl-ep for server-to-server integrations. That last one is your surface. It reads your key before anything else.

How big it is, and where that figure comes from. As at backend commit 4623d9e7ae, whose route table was regenerated on 7 October 2026, 1,205 routes carry the etl-ep segment: 1,058 endpoint methods across 177 controllers, covering about 320 resources. That takes in customers, suppliers and employees, items and inventory, companies, branches and locations, pricing, membership, payment gateways, and every business document type through one generic document endpoint. The platform’s own screens call a larger set, 9,494 backoffice-ep routes, with a signed-in user’s token. The figures are counted from the controllers’ mapping annotations and move whenever the backend adds a controller, so read them as a size.

Two cautions about the shape. The scope and layer segments are usually there, but 65 of those routes sit directly under /core2/dm/… or /core2/ms/…, and one uses the cm layer. Most paths also carry a sub-module, such as erp/fi/fi-items or erp/gen-doc/{docType}. So copy the path from the reference rather than composing it. A composed path that does not exist answers 417, not 404.

Where a resource is complete, it has the same six operations: create (POST …/etl-ep), update (PUT …/etl-ep), delete (DELETE …/etl-ep/{guid}), list (GET …/etl-ep), read one (GET …/etl-ep/{guid}) and filter (GET …/etl-ep/query). About a third of resources have all six, so check the reference before you assume one. Push and pull both run over the same endpoints, and webhooks give you the outbound event path.

Whether your key is accepted is decided by the route’s handler, not by the word in its path. Many routes built for a signed-in user also take a key when no token is sent, and a few public-ep routes still want a credential. Which credential works where has the table.

A key is a user. It carries no scope of its own and acts as exactly the user it was issued for. To stop an integration you delete its key or remove that user’s permissions; those are the two levers.

What happens behind a write

None of this shows in a response, and it decides whether your figures reach the books.

  1. Creating a document stores it. It does not post it. The create saves the document, numbers it and, for most document types, queues the create webhook. It starts no posting. A posting_status you send is stored as sent, so a document created as FINAL reads FINAL everywhere while no journal is ever written for it (since mid-October 2026 it is still handed to e-invoicing). Create as a draft.
  2. Finalise is a second call. PUT …/gen-doc/{docType}/etl-ep/update-posting-status/{guid} runs the checks a document must pass at FINAL, saves it, queues one primary job and answers. The answer comes back before that job runs. A 200 means final and numbered, not the journal exists.
  3. The primary job fans out per tenant. It queues the follow-on jobs your tenant subscribes to: journal, stock, cashbook, e-invoice, membership points and others. A company can narrow that list. That is why the same call posts more for one business than for another. How work actually runs traces the chain.
  4. Success is something you read back. The journal job writes posting_journal = POSTED on the document when it succeeds and writes nothing when it fails. A failure inside a follow-on job never comes back to your call.
  5. Your header guid is the retry guard. If you set the document’s guid yourself and keep it, a retry after a timeout is refused with GENERIC_DOC_HDR_OBJECT_GUID_ALREADY_EXISTS. That refusal means your first attempt landed. There is no idempotency header; this, plus client_key and client_source, is the mechanism.

Two routes set FINAL, and they differ. The etl-ep route above is the one built for your key. The screens use the sibling route without etl-ep, and that route also takes your key when you send no token. They do not do the same work:

…/etl-ep/update-posting-status/{guid}…/update-posting-status/{guid} (the screens')
A document already FINALChecked, saved and queued againRefused with “already been posted to FINAL”
Stock balance at FINALNot checkedChecked unless the body says not to
A foreign-currency documentSaved FINAL; the journal job declines it, so no journalA base-currency copy is made and posted
Maintained forServer-to-server callersThe screens, which change with the user interface

Whether all of these differences are intended is a question BigLedger’s engineers have been asked and have not yet answered, and which route these pages should recommend to you is still open. Until then, on the etl-ep route read posting_status before you call it, check stock yourself if it matters to you, and keep any foreign-currency invoice off it. The detail is in What the etl-ep Finalise does differently.

What BigLedger does not give you

Stated plainly, so you can plan around it rather than search for it.

The checks a screen makes live in the screen. BigLedger keeps one general endpoint per operation rather than one per screen rule, by design. The stock check above is one example; how much of an order a delivery has already used is another. Whatever your integration writes, the rule the screen would have applied is now yours to apply, or someone’s decision to skip. What the API leaves to you lists them, and Integration notes by module goes module by module.
There is no published client library for this surface. The public npm packages (blg-akaun-ts-lib, blg-akaun-ng-lib) are Angular libraries for applets running inside the BigLedger shell; they do not target the server-to-server endpoints. The Java client library is published to a private repository. You write HTTP yourself, which is not onerous, because most resources share the same six operations.
BigLedger runs no sandbox environment, and no self-service sign-up that yields one. Ask BigLedger for a test tenant before you build against a customer’s live data.
There is no machine-readable schema today. No OpenAPI document, no JSON Schema, no generated client. BigLedger has started generating a specification from the platform’s own code, and a reviewed public part of it is planned; no date has been set, and this section will link it when it is published. Until then, the Data API page sets out the shapes you need and the technique for the rest: read a record back and mirror what you get.
Pace your own jobs. The 429 the API returns is a database-overload signal: the database has run out of connection slots, and your job is competing with the tenant’s own users. Back off, retry with a growing wait, and cap your attempts. Requests stream and can run long; see the limits in the Data API.

When it goes wrong

What you seeUsual causeWhere the fix is
403 with a code starting AUTH_TOKEN_OR_The key itself: mistyped, deleted, expired, or the wrong secretAuthentication. Issue a new key only for these
403 with CLIENT_AUTH_USER_NOT_AUTHORIZEDThe key was accepted; its user lacks the permission, possibly only for the branch the document namesAsk the tenant administrator for the permission. A new key changes nothing
417 with a message beginning No static resourceThe path is not routedCopy the path from the reference instead of composing it
Finalise answered 200, and minutes later posting_journal is still blankA follow-on job stopped after the response, usually on a default GL code the company lacks; on the etl-ep route, a foreign-currency document is the other causeData API. Calling Finalise again repairs nothing
A retried create is refused with GENERIC_DOC_HDR_OBJECT_GUID_ALREADY_EXISTSYour first attempt landedRead the document back; do not create it again
429 with CLIENT_OVERLOAD_DATABASEThe database is out of connection slotsBack off and retry later, and move bulk work out of business hours
A webhook you expected never arrivesA topic subscription gets one attempt per event, and a failed delivery is goneWebhooks, plus a periodic pull to catch what was missed

Checks that prove it worked

Three reads settle it, and none needs anyone watching a screen.

  1. The connection. A one-row read of a resource you will really use, such as GET …/gen-doc/internal-sales-invoices/etl-ep/query?limit=1, answering OK_RESPONSE proves the key, the tenant code and the permission together. Change one character of the secret and the same call must answer AUTH_TOKEN_OR_ACCESS_ID_TOKEN_NOT_MATCHED; that proves your client sends the key you think it does.
  2. A posting. After Finalise, read the document back by its guid. posting_status reading FINAL is your call landing; posting_journal reading POSTED is the journal written.
  3. The day. Query yesterday’s records by your client_source, FINAL, and a date_txn range. The count and total should equal your own system’s, and no row should have a blank posting_journal. Somebody in finance owns this check, daily: for most feeds, nothing in BigLedger tells you one has stopped.

The integration in the rest of the product

  • What must exist first. A tenant code, and a user in that tenant holding the permissions your calls need; a tenant administrator creates that user, and the key you issue acts as exactly that user. For any business document, the company’s default GL codes must be set before the first FINAL, or the journal job stops after the call has already answered.
  • What becomes possible. A document you set FINAL posts into the same books the screens do, through the same follow-on jobs: the journal in Financial Accounting, the stock movement in Inventory, and, where the tenant subscribes, the e-invoice in E-Invoice. Nobody re-keys your web orders.
  • The siblings, and when to use which. Use these endpoints for a continuous feed; use the Batch and Import API for a one-off load. Use webhooks to hear about a change quickly, rather than polling for it. Use the E-Invoice API once a document is FINAL, to follow what became of the e-invoice. Build an applet instead when a person should do the work inside BigLedger. Use the Platform Library when you are writing a Java backend on BigLedger’s libraries rather than calling the API from outside.
  • What breaks if it is wrong. A document set FINAL without its default GL codes reads FINAL on every screen while the ledger is short by its amount until someone reposts it. Removing a user’s permissions stops every integration using a key issued to that user, which is why each integration gets a dedicated user.
  • What it is used alongside. Webhooks together with a periodic pull on updated_date_from: the pull makes your integration correct, the webhook only makes it fast. And on the receiving side, Sales That Arrive From Another System is the morning check the finance team runs against your feed; agree the daily count with them.

Asking BigLedger for a hand-off

When something only BigLedger can give you stands between you and a working integration, open an issue at BigLedger-Support/public. Say which resources you need to read or write, and in which direction. Anyone can read the tracker, so keep your tenant code and your own records out of the post; take anything that has to name your tenant to your BigLedger contact (see Support). The three things worth asking for by name are a test tenant, an integration user with a named permission set, and, if you need a resource that is not documented here, the current route for it.

If a page in this section tells you to call something that does not answer, that is a bug in the documentation and we want to hear about it.

Related documentation

Last updated on