Skip to content

Data API

Reference for the etl-ep endpoints — the server-to-server half of the BigLedger REST API. If you have not issued an access key yet, start with Getting Started.

Hosts

HostUse it for
https://api-etl.akaun.comData traffic. The ingress BigLedger’s own integrations use.
https://api.akaun.comSigning in and managing access keys

Both serve the same paths. Everything on this page is under api-etl.akaun.com.

Path grammar

/core2/{tnt|platform}/{dm|ms}/<module>/<resource>/<access-endpoint>
SegmentMeaning
tntTenant-scoped. Needs a tenantCode header. This is where business data lives.
platformPlatform-scoped: identity, applets, tenants. No tenantCode.
dmDomain model — the normal, current surface
msMicro-service — older, narrower endpoints
<module>erp, crm, inv, fi, cms, pgw, fm and others
<resource>The thing you are addressing
<access-endpoint>Who the endpoint is for

Access endpoints:

etl-epServer-to-server integrations. Yours.
backoffice-epA staff user working in the BigLedger shell
login-epA signed-in user acting on their own records
login-entity-epAn external party — a customer or supplier — acting on their own records
public-epDeliberately unauthenticated

Almost every business resource has both a backoffice-ep and an etl-ep variant, calling the same handler with the same permission checks. Build against etl-ep: it is the variant maintained for unattended callers, and it will not move when the user interface changes.

An unrecognised path returns 417, not 404, with "code": "SERVER_GENERAL_UNKNOWN_ERROR" and a message beginning No static resource. If you see that, the path is wrong.

The six operations

Most resources on this surface expose the same shape. Taking the item master as the example:

OperationRequest
CreatePOST /core2/tnt/dm/erp/fi/fi-items/etl-ep
UpdatePUT /core2/tnt/dm/erp/fi/fi-items/etl-ep
DeleteDELETE /core2/tnt/dm/erp/fi/fi-items/etl-ep/{guid}
List allGET /core2/tnt/dm/erp/fi/fi-items/etl-ep
Read oneGET /core2/tnt/dm/erp/fi/fi-items/etl-ep/{guid}
Filter and pageGET /core2/tnt/dm/erp/fi/fi-items/etl-ep/query

Many resources add extras — …/etl-ep/multi for batch create, …/etl-ep/query/count, …/etl-ep/query/snapshot, file attachment endpoints.

Do not assume all six exist on every resource. They usually do, but there are real gaps and they are not signposted. Entities are the one you will hit first: POST, PUT, GET and …/query all work at entity/{entityType}/etl-ep, but there is no delete on the typed path — delete is only on the type-agnostic DELETE /core2/tnt/dm/erp/entity/etl-ep/{guid}, and there is also PUT /core2/tnt/dm/erp/entity/etl-ep/inactivation/{guid} if you want to retire a record rather than remove it. Contacts are narrower still: on this surface they offer only POST …/contacts/etl-ep/multi. Probe the operation you need before you design around it — an absent route returns 417, so a probe is unambiguous.

What is on the surface

Around 950 endpoint methods across 156 controllers, covering roughly 287 resources. The groups you are most likely to need:

Master data

ResourcePath under /core2/tnt/dm/erp/
Customers, suppliers, employees, merchantsentity/{entityType}/etl-ep
All entities regardless of typeentity/etl-ep
Financial items (the sellable/purchasable item master)fi/fi-items/etl-ep
Inventory itemsinv/inv-items/etl-ep
Companiescompanies/etl-ep
Branchesbranches/etl-ep
Locationslocations/etl-ep
Contacts (batch create only)contacts/etl-ep/multi
Labels and label listslabels/etl-ep, label-lists/etl-ep
Pricing scheme linkspricing-scheme-link/etl-ep
Credit limits and credit termsfi/entity-sales-credit-limit/etl-ep and siblings

{entityType} is one of entities, customers, suppliers, employees, merchants — so a customer read is GET /core2/tnt/dm/erp/entity/customers/etl-ep/query.

Business documents

Every document type goes through one controller, keyed by a URL slug:

OperationRequest
List the document types this platform knowsGET /core2/tnt/dm/erp/gen-doc/etl-ep/server-doc-types
CreatePOST /core2/tnt/dm/erp/gen-doc/{docType}/etl-ep
Create manyPOST /core2/tnt/dm/erp/gen-doc/{docType}/etl-ep/multi
UpdatePUT /core2/tnt/dm/erp/gen-doc/{docType}/etl-ep
DeleteDELETE /core2/tnt/dm/erp/gen-doc/{docType}/etl-ep/{guid}
Read oneGET /core2/tnt/dm/erp/gen-doc/{docType}/etl-ep/{guid}
Filter and page, one typeGET /core2/tnt/dm/erp/gen-doc/{docType}/etl-ep/query
Filter and page, all typesGET /core2/tnt/dm/erp/gen-doc/etl-ep/query
VoidPUT /core2/tnt/dm/erp/gen-doc/{docType}/etl-ep/void/{guid}
Print to PDFGET /core2/tnt/dm/erp/gen-doc/{docType}/etl-ep/print-jasper-pdf/{guid}

There are 97 document-type slugs. The common ones:

{docType} slugDocument type
internal-sales-invoicesINTERNAL_SALES_INVOICE
internal-sales-cashbillsINTERNAL_SALES_CASHBILL
internal-sales-ordersINTERNAL_SALES_ORDER
internal-sales-returnsINTERNAL_SALES_RETURN
internal-sales-credit-notesINTERNAL_SALES_CREDIT_NOTE
internal-sales-debit-notesINTERNAL_SALES_DEBIT_NOTE
internal-purchase-ordersINTERNAL_PURCHASE_ORDER
internal-purchase-invoicesINTERNAL_PURCHASE_INVOICE
internal-purchase-goods-received-notesINTERNAL_PURCHASE_GOODS_RECEIVED_NOTE
internal-stock-adjustmentINTERNAL_STOCK_ADJUSTMENT
internal-payment-vouchersINTERNAL_PAYMENT_VOUCHER
internal-receipt-vouchersINTERNAL_RECEIPT_VOUCHER
server-doc-types returns the type names, not the URL slugs. It gives you INTERNAL_SALES_INVOICE; the URL wants internal-sales-invoices. Use it to confirm a type exists, then take the slug from the table above. If the type you need is not in that table, ask developers@bigledger.com for its slug — do not guess the pluralisation, because it is not consistent (internal-stock-adjustment is singular).

Other modules

crm/ carries membership cards, classes, points transactions and contacts. alg/cc/ carries the contact-centre resources — agents, conversations, channels, tasks. cms/ carries website and post resources. fm/ carries files and labels. pgw/ carries payment providers and payment transactions. All follow the same six-operation shape.

If a resource you need is not listed here, it may still exist — ask for the current route rather than guessing it.

Request and response format

Headers

AccessId: <10 characters>
AccessKey: <25 characters>
tenantCode: <the tenant>
Content-Type: application/json      # on POST and PUT

Full credential detail in Authentication.

Envelopes

Single object or non-paged list:

{ "code": "OK_RESPONSE", "data": { }, "message": "" }

Paged (…/query):

{
  "totalRecords": 5217,
  "offset": 0,
  "limit": 200,
  "code": "OK_RESPONSE",
  "message": "",
  "data": [ ]
}

code is a platform response code, not an HTTP status. Match on it.

Responses are streamed, not buffered. A large page arrives progressively and the connection stays open for the duration — parse incrementally rather than waiting for a complete body.

Responses are gzip-compressed above 1 KB. Send Accept-Encoding: gzip.

Bodies

Write bodies are containers: a header object plus arrays of related rows, keyed by the underlying table names.

{
  "bl_fi_generic_doc_hdr":  { },
  "bl_fi_generic_doc_line": [ ],
  "bl_fi_generic_doc_ext":  [ ],
  "bl_fi_generic_doc_link": [ ]
}

The same container type comes back from GET, so the reliable way to learn a write body is to read a record and mirror it. Dates are ISO-8601 strings, never epoch numbers.

Key names are not consistently pluralised. Document lines are bl_fi_generic_doc_line (singular); item lines are bl_fi_mst_item_lines (plural). Copy the keys from a real response.

Query parameters

…/query endpoints share a common set, plus per-resource filters.

ParameterDefaultNotes
limit100No enforced maximum. Responses stream, so a big page is a long response.
offset0
orderByrecord guidA column on the record, e.g. updated_date
orderASCASC or DESC; anything else is treated as ASC
calcTotalRecordsfalseCosts an extra COUNT. Send it on the first page only.
statusFilter by record status
excluded_statusesExclude statuses
search_wordFree-text search, where the resource supports it
querying_timezoneAsia/Kuala_LumpurThe zone date filters are interpreted in

Most of the resources you will sync also accept, in ISO-8601:

ParameterMeaning
updated_date_from / updated_date_toWhen the row last changed — use this for incremental pulls
created_date_from / created_date_toWhen the row was created
date_txn_from / date_txn_toThe business transaction date, on documents

Not every resource supports them. If a date filter does not change the result count, that resource does not implement it.

Document queries add server_doc_type, guid_comp, guid_branch, hdr_posting_status, doc_entity_hdr_guid, hdr_doc_ccy, and the correlation filters below.

Correlating with your own identifiers

Records carry three fields for your side of the mapping. They are yours to populate; BigLedger never writes them.

Field on the recordQuery parameterUse it for
client_keyhdr_client_keyYour primary key for the record
client_sourcehdr_client_sourceWhich of your systems it came from
client_valueAnything else you need to carry
GET …/gen-doc/internal-sales-invoices/etl-ep/query?hdr_client_source=storefront&hdr_client_key=WEB-2026-0043117
There is no idempotency-key header. Query on hdr_client_key before you create, and set client_key on everything you write. That is the only mechanism for not posting the same order twice.

Limits

Measured from the platform’s own configuration.

Request timeout2 hours for asynchronous request handling
Multipart upload2 GB per file and per request
Response compressiongzip above 1 KB
Page sizeDefaults to 100; no maximum enforced
Batch createNo maximum enforced on …/etl-ep/multi
Rate limitNone
Nothing throttles you. There is no rate limiter, no quota and no 429 anywhere in the platform. A runaway job will not be stopped for you — it will compete with the tenant’s own users for the same server. Pace your jobs, run bulk work outside business hours, and keep concurrency low. Integrations that work well page in the low hundreds to low thousands of rows and pause between pages.

Because there are no enforced maxima, the practical limits are the timeout and your own patience. A 2 GB multipart upload and a two-hour request are both technically allowed and both bad ideas.

Error codes

HTTPcodeMeaning
200OK_RESPONSESuccess
200/404OK_NODATAThe request was valid; nothing matched
400AUTH_TOKEN_OR_ACCESS_ID_WAS_NOT_PROVIDEDNo credential
400AUTH_TOKEN_OR_ACCESS_KEY_WAS_NOT_PROVIDEDAccessId without AccessKey
400CLIENT_TENANT_CODE_IS_EMPTYNo tenantCode header
401CLIENT_AUTH_NO_TOKENNo credential on a platform endpoint
403AUTH_TOKEN_OR_INVALID_ACCESS_IDNo key with that AccessId
403AUTH_TOKEN_OR_EXPIRED_ACCESS_IDThe key has passed its validDateEnd
403AUTH_TOKEN_OR_ACCESS_ID_TOKEN_NOT_MATCHEDWrong AccessKey for that AccessId
403CLIENT_AUTH_INVALID_TOKENMalformed or expired token
403CLIENT_AUTH_USER_NOT_AUTHORIZEDAuthenticated; the user lacks the permission
404CLIENT_TENANT_CODE_DOES_NOT_EXISTUnknown tenant
404/403CLIENT_VALIDATION_GUID_DOES_NOT_EXISTNo record with that GUID
400validation codesThe body failed a consistency check; data carries the failures
417SERVER_GENERAL_UNKNOWN_ERRORUnhandled error — or an unrecognised path

Check code before HTTP status. A validation failure and a permission failure can share a status but never share a code.

Related documentation

Last updated on