Skip to content
Webhooks

Webhooks

BigLedger can POST to a URL of yours when something changes in a tenant, so your system does not have to poll for it.

This page is for the developer whose system has to hear about a change without asking every few minutes. It says what the mechanism does and, at least as important, what it does not do: read What it does not do before you design around it.

The fast path, if you are ready to subscribe. On a topic subscription a delivery is one attempt, unsigned, and nothing tells you when one is lost (What it does not do), so step 4 is not optional.

  1. Find the topic record’s guid with GET /core2/tnt/dm/webhook-topic/query, from a row whose status is ACTIVE. If the list is empty, seed it once with POST /core2/tnt/dm/webhook-topic/populate-default. The seeded list is not the full set of topics that fire, so confirm the exact topic code for your event (Step 1 — make sure the topic exists).
  2. Create the subscription through the API with POST /core2/tnt/dm/webhook-subscription: the topic’s GUID, your URL, and both halves of the authentication header. The header value is stored and returned in clear text, so use one worth nothing except to your endpoint. A subscription created in the Web Hook screen arrives with no authentication header at all (Step 2 — create the subscription).
  3. Point it at a request-capture service first, then make the change the topic names. A sales invoice’s event fires when it is finalised, not when it is saved. You should see one POST carrying the header name and value you set (What success looks like).
  4. Re-read each record by its guid before acting on it, log on your side, and add the reconciliation pull on updated_date_from before you go live (What it does not do).

What it is

You register a subscription: a URL, a topic, and optionally one HTTP header for the receiver to authenticate with. When a matching change happens in that tenant, BigLedger POSTs a JSON body to your URL. Every attempt is written to a delivery log.

Topics are strings such as CUSTOMER_CREATED, INTERNAL_SALES_INVOICE_CREATED or INVENTORY_UPDATED. A subscription points at one topic. For several topics, register several subscriptions.

What it does not do

Four absences, all of which change how you should build.

There is no signature. BigLedger does not sign the payload — no HMAC, no shared signing secret, no timestamp or nonce. The only credential the receiver gets is one static header name and value that you chose when you created the subscription. Treat your endpoint as unauthenticated in practice: put it behind a hard-to-guess path, allowlist by source address if you can, and re-read the record through the API before you act on it rather than trusting the body.
There is no retry on a topic subscription. One attempt, ever. If your endpoint returns a 500, or times out, or is mid-deploy, the event is gone — there is no backoff, no dead-letter queue and no replay endpoint. Nothing tells you it was lost. Webhooks are a hint that something changed, not a delivery guarantee. Pair every subscription with a periodic reconciliation pull on updated_date_from (see Getting Started). That pull is what makes your integration correct; the webhook only makes it fast.
There is no alerting. A subscription record has fields for success and failure notification by email and SMS. They are loaded onto the in-memory subscription and never looked at again: no mail or SMS sender exists anywhere in the delivery path. No message is ever sent, on success or on failure.
You cannot browse the delivery log. Every attempt is recorded — what was sent, what came back, the HTTP status, and the error on a transport failure — but the endpoint that lists those records reads the wrong database, so the list always comes back empty, and a second listing route that reads the right one has a malformed query and does not return them either. Fetching one by its identifier works, and you have no way to obtain an identifier. In practice: log on your side, and treat the platform’s delivery log as unavailable until this is fixed.

A second, separate feed: e-invoice lifecycle events

Everything above describes topic subscriptions. E-invoice lifecycle events are a different mechanism with different guarantees, and a developer who reads “there is no retry” above and applies it here will be wrong.

What it carries. The life of an e-invoice: a pool or batch submission accepted or refused, a submission sent to LHDN, validation valid or invalid, a cancellation submitted, accepted or failed. It also carries the JSON document import’s own events: received, processed, created, creation failed.

How you get it. It is switched on per company, not per tenant, and nothing is recorded for a company that has it off. No screen we could find sets that switch, so ask BigLedger to turn it on for the company you need. Once it is on, each event waits in a queue in your tenant, and you choose how to receive it:

  • Pull. With no subscription, the event stays in the queue for you to read through GET …/generic-webhook-queues/etl-ep (and /query, /{guid}). A background job moves these rows into history whether or not you have read them, and history is readable through GET …/generic-webhook-histories/etl-ep. Neither GET deletes anything.
  • Push. Create a subscription on the topic EINVOICE_LIFECYCLE, optionally limited to the event codes you want. Each event is then POSTed to your URL with an X-BLG-Event header naming it, plus the one header from the subscription. A failed delivery is retried: five attempts in all, 1, 5, 15 and 60 minutes apart. Every attempt, success or failure, gets a history row you can read through the history endpoint. Success on this feed is a 2xx reply whose JSON body carries "code": 10000; any other 2xx reply is treated as a refusal and is not retried, because the same body would be refused again. A receiver for this feed answers with that body, not a bare 200.

What it will not do. Both the move to history and the push delivery are background jobs, and neither is on a tenant’s clock by default. Without the move job, pulled events stay in the queue indefinitely; without the delivery job, pushed events wait and are never sent. Check with BigLedger that the job you depend on is on your tenant’s schedule before you build on it. Push delivery is still unsigned, so the advice under There is no signature applies here too.

Setting one up

Step 1 — make sure the topic exists

A subscription points at a topic record, not at a topic string, so the record has to exist first. The topic codes most integrations use are in Topics, at the end of this page.

curl -s "https://api-etl.akaun.com/core2/tnt/dm/webhook-topic/query?limit=200" \
  -H "AccessId: $BLG_ACCESS_ID" -H "AccessKey: $BLG_ACCESS_KEY" -H "tenantCode: $BLG_TENANT_CODE"

The response is the usual list envelope, one bl_webhook_topic_hdr per topic:

{
  "totalRecords": 1,
  "offset": 0,
  "limit": 200,
  "code": "OK_RESPONSE",
  "message": "",
  "data": [
    {
      "bl_webhook_topic_hdr": {
        "guid": "5b0e7c2a-91d4-4f3e-8a61-2c7d9e4b1f08",
        "topic_code": "INTERNAL_SALES_INVOICE_CREATED",
        "topic_name": "Internal Sales Invoice Created",
        "status": "ACTIVE",
        "property_json": {}
      }
    }
  ]
}

totalRecords is a count only when you add calcTotalRecords=true; without it this endpoint answers 1 whatever the list holds, so ignore it. A topic you hold no read permission on still comes back, carrying only its guid and "status": "PERMISSION_DENIED" — check status before you take a guid from the list.

If the list is empty, the tenant has never been seeded. Seeding is one call and it is idempotent:

curl -s -X POST "https://api-etl.akaun.com/core2/tnt/dm/webhook-topic/populate-default" \
  -H "AccessId: $BLG_ACCESS_ID" -H "AccessKey: $BLG_ACCESS_KEY" -H "tenantCode: $BLG_TENANT_CODE"

It inserts the built-in topics the tenant is missing and answers OK_NODATA with no data, whether it inserted all of them, some or none:

{ "code": "OK_NODATA", "data": null, "message": "" }

That inserts the platform’s built-in list of 54 topics. Take the guid of the one you want from the query above.

The built-in list is a seed, not the full set — and it does not match what actually fires. More than seventy topic codes that BigLedger genuinely emits are absent from the built-in list, so populate-default never creates them and nothing in the product lists them. Conversely, at least one seeded topic can never fire: the built-in list spells the branch-deleted topic BRANCH_DELTED while the platform emits BRANCH_DELETED, so a subscription to the offered topic receives nothing, forever.

If the event you need is not in the seeded list, do not assume it does not exist — open an issue at BigLedger-Support/public and ask for the exact topic code, then create the topic record yourself with POST /core2/tnt/dm/webhook-topic. The code must match the platform’s string exactly; there is no fuzzy matching and no error if it never matches.

Step 2 — create the subscription

curl -s -X POST "https://api-etl.akaun.com/core2/tnt/dm/webhook-subscription" \
  -H "AccessId: $BLG_ACCESS_ID" -H "AccessKey: $BLG_ACCESS_KEY" \
  -H "tenantCode: $BLG_TENANT_CODE" -H "Content-Type: application/json" \
  -d '{
        "bl_webhook_subscription_hdr": {
          "topic_hdr_guid": "<guid of the topic record>",
          "url": "https://hooks.gadgetsphere.example/blg/sales-invoice-created",
          "auth_header_name": "X-GadgetSphere-Token",
          "auth_header_value": "<a long random string>",
          "status": "ACTIVE"
        }
      }'

The answer is the subscription as stored, with the guid you need to update or delete it later:

{
  "code": "OK_RESPONSE",
  "data": {
    "bl_webhook_subscription_hdr": {
      "guid": "c41e8b27-6f05-4d93-a2b8-7e1d5c9f3a60",
      "topic_hdr_guid": "5b0e7c2a-91d4-4f3e-8a61-2c7d9e4b1f08",
      "url": "https://hooks.gadgetsphere.example/blg/sales-invoice-created",
      "auth_header_name": "X-GadgetSphere-Token",
      "status": "ACTIVE"
    }
  },
  "message": ""
}
FieldNotes
topic_hdr_guidThe topic record’s GUID, not the topic code
urlMaximum 255 characters. Not validated — a typo produces a subscription that silently never delivers.
auth_header_nameA valid HTTP header name. No spaces.
auth_header_valueThe shared secret. Maximum 255 characters.
statusACTIVE. Only DELETED stops the subscription; there is no pause. The topic record has a status of its own, and events stop when it is not ACTIVE (It was working, then it stopped).
Create subscriptions through the API, not through the product. The Web Hook screen offers three fields — a title, a URL and a topic — and the field labelled Web Hook Title is stored in the auth_header_name column. It never sets auth_header_value, and BigLedger only sends the header when both halves are present. A subscription created in the user interface therefore arrives at your endpoint with no authentication header at all. The screen also makes the URL read-only once saved, so changing a destination means deleting and recreating. The API sets all four fields properly.

Managing subscriptions:

CreatePOST /core2/tnt/dm/webhook-subscription
UpdatePUT /core2/tnt/dm/webhook-subscription
DeleteDELETE /core2/tnt/dm/webhook-subscription/{guid}
ListGET /core2/tnt/dm/webhook-subscription
Read oneGET /core2/tnt/dm/webhook-subscription/{guid}
FilterGET /core2/tnt/dm/webhook-subscription/query

These need the tenant permissions API_TNT_DM_WEBHOOK_SUBSCRIPTION_CREATE, _UPDATE, _DELETE and _READ, or the tenant owner or administrator permission (TNT_TENANT_OWNER, TNT_TENANT_ADMIN). The administrator rank on the user’s tenant link is not the same thing: a user who holds that rank and none of these codes is refused with 403. Step 1 additionally needs API_TNT_DM_WEBHOOK_TOPIC_READ to list topics and API_TNT_DM_WEBHOOK_TOPIC_CREATE to seed or create them. Ask for all of them when you ask for the integration user — a key that can read records cannot manage subscriptions unless it was granted these too.

auth_header_value is stored and returned in clear text, and it is a filterable query parameter. Anyone who can read subscriptions in the tenant can read the secret. Use a value that is worth nothing except to your endpoint, and rotate it on the same schedule as your access key.

What success looks like

Thirty seconds, and worth spending before you write any receiving code.

  1. Point the subscription at a request-capture service (https://webhook.site/… or your own nc -l 8080) instead of your real endpoint.
  2. Make the change the topic names — for CUSTOMER_CREATED, create one customer in the tenant.
  3. Within a second or two you should see one POST, Content-Type: application/json, carrying the header name and value you set, and a body whose top-level key is a table name such as bl_fi_mst_entity_hdr.

Troubleshooting: nothing arrives

Work backwards in this order: the topic record exists, is ACTIVE, is the only one with its code, and its topic_code matches the platform’s string exactly; the subscription’s status is ACTIVE; the url has no typo (it is not validated, so a wrong one fails silently and forever); the topic is one the platform actually emits, not merely one populate-default seeded. If the POST arrives but has no authentication header, the subscription was created in the Web Hook screen rather than through the API — delete it and create it again with both header halves.

It was working, then it stopped

BigLedger never pauses a subscription on its own: only a PUT or a DELETE changes one, failures are not counted, and nothing is switched off after them. So a feed that goes quiet means something changed. Check in this order:

  1. The topic is still ACTIVE, and there is only one record for its code. Document, customer, item and membership-card events fire only while the topic record reads exactly ACTIVE, whatever the subscription says. Nothing makes topic_code unique, and the lookup takes the first record it finds, so a second POST of a topic that already existed can leave a working subscription on the record that is no longer found. List the topics (Step 1) and look for two rows with your code.
  2. The subscription is not DELETED. Each create, update and delete leaves a change record with its date, not who made it.
  3. The event really happened. A sales invoice sends nothing until it is FINAL; other document types send _CREATED when the draft is saved (What arrives).
  4. Your receiver still accepts the header. The POST carries only the header stored on the subscription. If your side rotated that secret and nobody PUT the new value onto the subscription, every delivery is refused with a 401, recorded, and never retried. Revoking the BigLedger API key your integration uses does not stop deliveries; it stops your reconciliation pull.
  5. Your receiver answers at once. Deliveries go out from a small fixed pool with a 60-second connect timeout and no read timeout, so an endpoint that accepts the connection and never answers holds a sender and can delay the events behind it. Reply 200 with a short body first and do the work afterwards.

Then catch up with the reconciliation pull. Start it from the last pull that completed, not from the last event you received: deliveries go out in parallel and can arrive out of order, so a record changed before your last event may never have arrived. Overlap the window a little and skip records you already hold (updated_date_from includes its own instant). It is the only way to recover what was missed; there is no replay. On the e-invoice feed, rows still waiting in …/generic-webhook-queues with past times and no new history rows mean the delivery job is not running on your tenant: ask BigLedger to check its clock.

How to build on this

Once a test delivery arrives, build the receiver around the four absences above. The pattern that works:

  1. Subscribe to the topics you care about, one URL each.
  2. Treat every delivery as “something may have changed” — take the GUID from the body, and re-read the record through …/etl-ep/{guid} before acting.
  3. Reconcile on a schedule with an updated_date_from pull. This catches everything the webhook dropped, and it is the part you must not skip.
  4. Log every delivery on your side, with the raw body. The platform’s own log is not readable.
  5. Return 200 fast, with a short body (a reply with no body is logged as a failure). Queue the work. On the e-invoice feed the body must carry "code": 10000 (above).

A minimal receiver

Points 4 and 5, and the start of point 2, in about fifty lines of Python with nothing to install. It checks the header you set on the subscription, refuses an oversized body, writes each new body to its own log before it answers 200, skips a body it has already accepted, and hands the guid to a worker. The worker only prints it: that is where your code re-reads the record:

# receiver.py: a minimal BigLedger webhook receiver (Python 3, standard library only).
# It speaks plain HTTP on port 8080: put it behind your HTTPS reverse proxy.
import hashlib, hmac, json, os, queue, threading
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

HEADER = "X-GadgetSphere-Token"            # the subscription's auth_header_name
SECRET = os.environ["GS_WEBHOOK_TOKEN"]    # its auth_header_value, never in the code
MAX_BODY = 5 * 1024 * 1024                 # a one-line invoice is about 25 KB
seen, lock = set(), threading.Lock()       # bodies already accepted; use a table in production
work = queue.Queue()

class Hook(BaseHTTPRequestHandler):
    def reply(self, code):
        self.send_response(code)
        self.send_header("Content-Length", "2")
        self.end_headers()
        self.wfile.write(b"OK")            # always a body: a reply without one is logged as a failure

    def do_POST(self):
        if not hmac.compare_digest(self.headers.get(HEADER, ""), SECRET):
            return self.reply(401)
        size = int(self.headers.get("Content-Length") or 0)
        if size > MAX_BODY:
            return self.reply(413)
        raw = self.rfile.read(size)
        digest = hashlib.sha256(self.path.encode() + raw).hexdigest()
        with lock:
            new = digest not in seen       # the same body twice is one event
            if new:
                seen.add(digest)
                with open("webhooks.log", "ab") as log:   # your delivery log, written before you answer
                    log.write(self.path.encode() + b" " + raw.replace(b"\n", b" ") + b"\n")
        if new:
            work.put((self.path, raw))
        self.reply(200)                    # answer at once, work later

def worker():
    while True:
        path, raw = work.get()
        try:
            body = json.loads(raw)
        except ValueError:
            body = None                    # a few topics send a sentence, not JSON
        hdr = body.get("bl_fi_generic_doc_hdr") if isinstance(body, dict) else None
        if hdr:
            # Your code goes here: re-read the record by its guid before acting, e.g.
            # GET .../gen-doc/internal-sales-invoices/etl-ep/<guid>
            print(path, "changed:", hdr["guid"])
        else:
            print(path, "body without a document header:", raw[:80])

threading.Thread(target=worker, daemon=True).start()
ThreadingHTTPServer(("", 8080), Hook).serve_forever()

What it keeps in memory is lost on a restart: the queue and the list of bodies already seen. The log file is what survives: replay it after a restart, and in production keep both in a table. Two things it cannot do, because the platform does not: tell a forged call from a real one beyond the shared header, or learn of an event that was not delivered (What it does not do), which is why point 3 stays on your schedule. Before you rely on it, point a test subscription at it and make one change in the tenant, as in What success looks like above.

What arrives at your endpoint

This is the body the receiver above takes apart, and the shapes that would break a parser that expects only one. A GadgetSphere sales invoice arrives like this, trimmed to the fields most receivers read. The whole body is below it.

A sales invoice’s event fires when it is finalised, not when it is saved. Saved as a draft through the screen or POST …/internal-sales-invoices/etl-ep, a sales invoice sends nothing; its INTERNAL_SALES_INVOICE_CREATED is queued when it is set to FINAL, and the body is the invoice as it stands then. Other document types send _CREATED when the draft is saved.

POST /blg/sales-invoice-created HTTP/1.1
Host: hooks.gadgetsphere.example
Content-Type: application/json; charset=UTF-8
X-GadgetSphere-Token: <your auth_header_value>

{
  "bl_fi_generic_doc_hdr": {
    "guid": "9c1e4a7b-2f35-4d8e-a6b0-3e7d5f9a1c42",
    "server_doc_type": "INTERNAL_SALES_INVOICE",
    "code_company": "GS",
    "code_branch": "GS-KV-01",
    "doc_entity_hdr_guid": "3b8e1f6c-9d24-4a7e-b153-7f0a2e8c4d69",
    "doc_ccy": "MYR",
    "amount_txn": 1499.00,
    "posting_status": "FINAL",
    "date_txn": "2026-10-18T03:12:44.518Z",
    "status": "ACTIVE",
    "revision": "5e0a8c3d-7f19-4b62-9d4e-1a6c8f2b0e57",
    "updated_date": "2026-10-18T03:14:02.781204Z"
  },
  "bl_fi_generic_doc_event": [],
  "bl_fi_generic_doc_ext": [],
  "bl_fi_generic_doc_link": [],
  "bl_fi_generic_doc_line": [
    {
      "guid": "d5a2f7c1-6e93-4b08-a4d6-1c8e3f5b9a27",
      "item_code": "AUD-TWS-PRO-BLK",
      "item_name": "Wireless earbuds, black",
      "quantity_base": 1,
      "amount_txn": 1499.00
    }
  ],
  "bl_fi_generic_doc_budget_sub_line": []
}

Your endpoint’s reply is all BigLedger reads back:

HTTP/1.1 200 OK
Content-Length: 2

OK

BigLedger stores the status code and the body you return in its delivery log, and does nothing else with them. Return a body, even OK: a reply with none, such as 204 No Content, is logged as a failed delivery with no status code, and on a topic subscription nothing is retried.

The whole body: every column of both tables, null included (about 25 KB)

The body is the full container, the same object the document query returns for one invoice: 355 header columns and 357 on each line at the time of writing, most of them null on a new invoice. Values are GadgetSphere’s; the keys and their order are a real record’s.

{
  "bl_fi_generic_doc_hdr": {
    "guid": "9c1e4a7b-2f35-4d8e-a6b0-3e7d5f9a1c42",
    "seller_order_alloc_queue": null,
    "entity_branch_hdr_guid": null,
    "intercompany_hdr_guid": null,
    "intercompany_line_guid": null,
    "doc_source_type": "INTERNAL",
    "txn_type": null,
    "txn_code": null,
    "far_hdr_guid": null,
    "namespace": null,
    "app_id": null,
    "server_doc_type": "INTERNAL_SALES_INVOICE",
    "server_doc_1": "1004817",
    "server_doc_2": null,
    "server_doc_3": null,
    "server_doc_4": null,
    "server_doc_5": null,
    "server_doc_location": null,
    "client_guid": null,
    "client_doc_type": null,
    "client_doc_1": null,
    "client_doc_2": null,
    "client_doc_3": null,
    "client_doc_4": null,
    "client_doc_5": null,
    "guid_comp": "1f6a2c8e-7b3d-4e95-9a10-5c4d8e2b7f31",
    "guid_branch": "6e2b9d14-a3c7-4f08-b5e1-8d0c7a4f2e96",
    "guid_store": null,
    "guid_profit_center": null,
    "guid_segment": null,
    "guid_project": null,
    "guid_dimension": null,
    "doc_level": null,
    "foreign_ccy": null,
    "base_doc_guid": null,
    "base_doc_ccy": null,
    "base_doc_xrate": null,
    "base_doc_xrate_inverse": null,
    "doc_ccy": "MYR",
    "amount_cogs": null,
    "amount_std": null,
    "amount_discount": null,
    "amount_net": 1499.00,
    "amount_tax_gst": null,
    "amount_tax_wht": null,
    "amount_txn": 1499.00,
    "amount_json": {},
    "amount_open_balance": 1499.00,
    "amount_gst_balance": null,
    "amount_wht_balance": null,
    "amount_internal_settlement": null,
    "amount_signum": 1.0,
    "doc_reference": null,
    "doc_desc": null,
    "doc_remarks": null,
    "doc_reference_tax_num": null,
    "doc_reference_tax_date": null,
    "doc_entity_hdr_guid": "3b8e1f6c-9d24-4a7e-b153-7f0a2e8c4d69",
    "doc_entity_hdr_json": {},
    "doc_entity_line_guid": null,
    "doc_entity_line_json": {},
    "gst_entity_hdr_guid": null,
    "gst_entity_hdr_json": {},
    "gst_entity_tax_num": null,
    "gst_entity_type": null,
    "gst_entity_contact_json": {},
    "display_entity_json": {},
    "delivery_entity_json": {},
    "foreign_references_json": {},
    "label_json": {},
    "status_json": [],
    "property_json": {},
    "status_client": null,
    "status_server": null,
    "display_entity_guid": null,
    "delivery_entity_guid": null,
    "billing_json": {},
    "credit_terms_json": {
      "creditterms": "30"
    },
    "date_txn": "2026-10-18T03:12:44.518Z",
    "date_finalized": "2026-10-18T03:14:02.774Z",
    "date_discarded": null,
    "date_voided": null,
    "date_txn_logic": null,
    "process": null,
    "log_json": {},
    "status": "ACTIVE",
    "revision": "5e0a8c3d-7f19-4b62-9d4e-1a6c8f2b0e57",
    "vrsn": null,
    "contact_key_guid": null,
    "member_guid": null,
    "contact_hdr_guid": null,
    "pgw_merchant_ref_no": null,
    "pgw_txn_hdr_guid": null,
    "pgw_txn_status": null,
    "delivery_driver_guid": null,
    "delivery_driver_code": null,
    "created_by_subject_guid": "a7d3c5e9-1b42-4f6a-8e07-2c9b4d6f1a83",
    "updated_by_subject_guid": "a7d3c5e9-1b42-4f6a-8e07-2c9b4d6f1a83",
    "created_date": "2026-10-18T03:12:45.102331Z",
    "updated_date": "2026-10-18T03:14:02.781204Z",
    "module_guid": null,
    "applet_guid": null,
    "acl_config": null,
    "acl_policy": null,
    "code_company": "GS",
    "code_branch": "GS-KV-01",
    "code_location": null,
    "posting_status": "FINAL",
    "posting_inventory": null,
    "posting_journal": null,
    "posting_tax_gst": null,
    "posting_tax_wht": null,
    "posting_membership": null,
    "posting_running_no": null,
    "posting_cashbook": null,
    "posting_sales_commission_queue": null,
    "posting_membership_points_commission_queue": null,
    "posting_bundle": null,
    "posting_finance_charge_amount": null,
    "due_date": null,
    "client_key": null,
    "client_source": null,
    "client_entity_code": null,
    "client_doc_date_01": null,
    "client_doc_date_02": null,
    "client_doc_date_03": null,
    "client_doc_date_04": null,
    "client_doc_date_05": null,
    "client_doc_status_01": null,
    "client_doc_status_02": null,
    "client_doc_status_03": null,
    "client_doc_status_04": null,
    "client_doc_status_05": null,
    "client_value": null,
    "pic_entity_01": null,
    "pic_entity_02": null,
    "pic_entity_03": null,
    "guid_store_2": null,
    "arap_pns_amount": null,
    "arap_stlm_amount": null,
    "arap_doc_open": null,
    "arap_contra": null,
    "arap_bal": null,
    "tracking_id": null,
    "delivery_preferences": null,
    "delivery_time_preferred": null,
    "delivery_date_preferred": null,
    "delivery_date_expected": null,
    "delivery_date_planned": null,
    "delivery_date_actual": null,
    "cfg_production": null,
    "cfg_delivery": null,
    "del_region_hdr_guid": null,
    "del_region_hdr_reg_code": null,
    "del_region_hdr_state": null,
    "track_production_logic": null,
    "track_production_table": null,
    "track_production_guid": null,
    "track_production_id": null,
    "track_production_time_estimated": null,
    "track_production_time_actual": null,
    "track_production_time_planned": null,
    "track_production_pic_name": null,
    "track_production_pic_contact": null,
    "track_production_remarks": null,
    "track_production_status": null,
    "track_delivery_logic": null,
    "track_delivery_table": null,
    "track_delivery_guid": null,
    "track_delivery_id": null,
    "track_delivery_time_estimated": null,
    "track_delivery_time_actual": null,
    "track_delivery_time_planned": null,
    "track_delivery_pic_name": null,
    "track_delivery_pic_contact": null,
    "track_delivery_remarks": null,
    "track_delivery_status": null,
    "reseller_entity_hdr_guid": null,
    "reseller_member_hdr_guid": null,
    "sales_entity_hdr_guid": null,
    "sales_member_hdr_guid": null,
    "sales_lead_guid": null,
    "sales_lead_name": null,
    "device_guid": null,
    "delivery_required": null,
    "delivery_option": null,
    "ready_to_ship": null,
    "track_delivery_date_requested": null,
    "posting_mto": null,
    "pickup_branch_guid": null,
    "void_reason": null,
    "landed_cost_amount": null,
    "apportion_type": null,
    "valid_days": null,
    "end_datetime": null,
    "delivery_branch_guid": null,
    "delivery_branch_code": null,
    "delivery_location_guid": null,
    "delivery_location_code": null,
    "allow_line_diff_from_hdr_del_branch": false,
    "wf_process_hdr_guid": null,
    "wf_process_status_guid": null,
    "wf_process_status_code": null,
    "wf_resolution_guid": null,
    "wf_resolution_code": null,
    "doc_internal_remarks": null,
    "doc_external_remarks": null,
    "custom_status": null,
    "marketplace_status": null,
    "url_key": null,
    "payment_url_key": null,
    "payment_url_expiry": null,
    "notification_email": null,
    "notification_phone": null,
    "created_by_name": null,
    "updated_by_name": null,
    "sales_entity_hdr_name": null,
    "sales_entity_hdr_code": null,
    "pic_entity_01_name": null,
    "pic_entity_02_name": null,
    "pic_entity_03_name": null,
    "client_salesman_code": null,
    "cancel_date": null,
    "cancel_info_json": null,
    "mkt_payment_status": null,
    "packing_status": null,
    "mkt_order_no": null,
    "posting_trade_in": null,
    "posting_fixed_asset": null,
    "bill_to": null,
    "xtn_process_hdr_guid": null,
    "xtn_process_status_guid": null,
    "xtn_process_resolution_guid": null,
    "xtn_process_status_code": null,
    "xtn_process_resolution_code": null,
    "xtn_workflow_updated_at": null,
    "marketplace_shipping_fee": null,
    "intercompany_execute": false,
    "intercompany_target_doc_type": null,
    "src_intercompany_hdr_guid": null,
    "intercompany_settings_json": null,
    "shipping_fee_courier_price": null,
    "shipping_fee_onsite_price": null,
    "shipping_fee_self_pickup_price": null,
    "installation_fee": null,
    "print_count": null,
    "print_status": null,
    "print_date": null,
    "forex_doc_hdr_guid": null,
    "remarks_category": null,
    "vehicle_hdr_guid": null,
    "vehicle_registration_no": null,
    "pet_hdr_guid": null,
    "pet_id": null,
    "pet_name": null,
    "svc_record_01": null,
    "svc_record_02": null,
    "svc_issue_hdr_guid": null,
    "votebook_guid": null,
    "budget_item_guid": null,
    "budget_register_guid": null,
    "budget_fiscal_year_guid": null,
    "budget_fiscal_period_guid": null,
    "posting_budget": null,
    "xtn_doc_ref_1": null,
    "xtn_doc_ref_2": null,
    "xtn_doc_ref_3": null,
    "xtn_doc_ref_4": null,
    "xtn_doc_ref_5": null,
    "xtn_doc_ref_1_date": null,
    "xtn_doc_ref_2_date": null,
    "xtn_doc_ref_3_date": null,
    "xtn_doc_ref_4_date": null,
    "xtn_doc_ref_5_date": null,
    "einvoice_self_billed": null,
    "original_einvoice_ref_no": null,
    "einvoice_main_document_ref_irb_guid": null,
    "einvoice_to_irb_hdr_guid": null,
    "einvoice_to_irb_hdr_ref_no": null,
    "clone_source_gen_doc_hdr_guid": null,
    "einvoice_shipping_entity_json": null,
    "trade_customs_ref_no": null,
    "trade_incoterms": null,
    "trade_agreement": null,
    "trade_authorization_number": null,
    "trade_custom_ref_no_2": null,
    "trade_other_charges_desc": null,
    "trade_other_charges_amount": null,
    "trade_country_of_origin": null,
    "tax_exemption_details": null,
    "tax_exemption_amount": null,
    "einvoice_shipping_entity_guid": null,
    "pin_code": null,
    "pin_type": null,
    "pin_hash": null,
    "pin_algorithm": null,
    "einvoice_supplier_entity_hdr_guid": null,
    "einvoice_buyer_entity_hdr_guid": null,
    "einvoice_delivery_entity_hdr_guid": null,
    "einvoice_tax_id_no": null,
    "id_no": null,
    "phone": null,
    "billing_entity_branch_hdr_guid": null,
    "delivery_entity_branch_hdr_guid": null,
    "forex_source_hdr_guid": null,
    "forex_source_history_guid": null,
    "einvoice_buyer_entity_hdr_json": null,
    "einvoice_supplier_entity_hdr_json": null,
    "einvoice_submission_type": null,
    "skip_einvoice": false,
    "billing_entity_hdr_guid": null,
    "einvoice_entity_hdr_guid": null,
    "einvoice_entity_hdr_json": null,
    "guid_glcode": null,
    "multi_po_session_guid": null,
    "entity_branch_code": null,
    "einvoice_to_irb_lhdn_document_guid": null,
    "einvoice_main_document_ref_to_irb_lhdn_document_guid": null,
    "einvoice_number": null,
    "einvoice_billing_frequency": null,
    "einvoice_billing_period_start": null,
    "einvoice_billing_period_end": null,
    "file_batch_id": null,
    "einvoice_submission_type_2": null,
    "einvoice_submission_type_2_date_time": null,
    "einvoice_datetime": null,
    "demographic_race": null,
    "demographic_age_range": null,
    "demographic_gender": null,
    "demographic_education_level": null,
    "demographic_income_level": null,
    "demographic_employment_status": null,
    "demographic_occupation": null,
    "demographic_marital_status": null,
    "demographic_language_preference": null,
    "demographic_geography_region": null,
    "demographic_interests": null,
    "crm_sales_channel_hdr_guid": null,
    "crm_sales_channel_hdr_code": null,
    "crm_promotion_hdr_guid": null,
    "crm_promotion_hdr_code": null,
    "shipping_recipient_entity_hdr_json": null,
    "courier_service_provider_link": null,
    "shipping_recipient_entity_hdr_guid": null,
    "original_invoice_ref_no": null,
    "original_invoice_ref_no_column_name": null,
    "original_invoice_date": null,
    "posting_ko": null,
    "doc_reference_date_txn": null,
    "skip_historical_aging": false,
    "use_date_txn": false,
    "custom_field_json": {},
    "amount_discount_line": null,
    "amount_discount_additional": null,
    "einvoice_to_irb_hdr_grouped_guid": null
  },
  "bl_fi_generic_doc_event": [],
  "bl_fi_generic_doc_ext": [],
  "bl_fi_generic_doc_link": [],
  "bl_fi_generic_doc_line": [
    {
      "guid": "d5a2f7c1-6e93-4b08-a4d6-1c8e3f5b9a27",
      "entity_branch_hdr_guid": null,
      "sales_entity_hdr_guid": null,
      "pic_entity_01": null,
      "pic_entity_02": null,
      "pic_entity_03": null,
      "sales_entity_hdr_name": null,
      "sales_entity_hdr_code": null,
      "pic_entity_01_name": null,
      "pic_entity_02_name": null,
      "pic_entity_03_name": null,
      "intercompany_hdr_guid": null,
      "intercompany_line_guid": null,
      "pricing_scheme_link_guid": null,
      "server_doc_type": null,
      "client_doc_type": null,
      "generic_doc_hdr_guid": "9c1e4a7b-2f35-4d8e-a6b0-3e7d5f9a1c42",
      "guid_parent": null,
      "guid_comp": "1f6a2c8e-7b3d-4e95-9a10-5c4d8e2b7f31",
      "guid_branch": "6e2b9d14-a3c7-4f08-b5e1-8d0c7a4f2e96",
      "guid_store": null,
      "guid_profit_center": null,
      "guid_segment": null,
      "guid_project": null,
      "guid_dimension": null,
      "guid_glcode": null,
      "far_hdr_guid": null,
      "label_json": null,
      "foreign_ccy": null,
      "base_doc_line_guid": null,
      "base_doc_line_ccy": null,
      "base_doc_line_xrate": null,
      "doc_ccy": "MYR",
      "qty_reserve_adj": null,
      "amount_cogs": null,
      "amount_std": null,
      "amount_discount": null,
      "amount_net": 1499.00,
      "tax_gst_code": null,
      "tax_gst_type": null,
      "tax_gst_rate": null,
      "amount_tax_gst": null,
      "tax_wht_type": null,
      "tax_wht_code": null,
      "tax_wht_rate": null,
      "amount_tax_wht": null,
      "amount_txn": 1499.00,
      "amount_json": null,
      "amount_open_balance": null,
      "amount_signum": null,
      "item_guid": "8f4c2a6e-1d93-4b7f-a052-6e3d9b1c7f48",
      "item_code": "AUD-TWS-PRO-BLK",
      "item_machine_code": null,
      "item_name": "Wireless earbuds, black",
      "item_desc": null,
      "item_remarks": null,
      "item_reference_no": null,
      "item_custom_template_json": null,
      "item_custom_data_json": null,
      "item_property_json": null,
      "item_child_json": null,
      "item_to_item_link_json": null,
      "sort_code": null,
      "line_option_json": null,
      "txn_type": null,
      "txn_code": null,
      "reference_no": null,
      "quantity_signum": null,
      "quantity_base": 1,
      "unit_price_by_uom": null,
      "qty_by_uom": null,
      "unit_disc_by_uom": null,
      "client_doc_status_01": null,
      "client_doc_status_02": null,
      "client_doc_status_03": null,
      "client_doc_status_04": null,
      "client_doc_status_05": null,
      "client_data_source": null,
      "client_item_guid": null,
      "client_hashed_value": null,
      "line_integrity_token": null,
      "client_item_code": null,
      "client_source": null,
      "client_key": null,
      "client_value": null,
      "client_so_item_pkid": null,
      "uom_json": null,
      "line_property_json": null,
      "position_id": null,
      "date_txn": null,
      "log_json": {},
      "status": "ACTIVE",
      "revision": "0b7d4e2a-9c61-4f38-a5e7-3d1f8b6c2a90",
      "vrsn": null,
      "created_by_subject_guid": "a7d3c5e9-1b42-4f6a-8e07-2c9b4d6f1a83",
      "updated_by_subject_guid": "a7d3c5e9-1b42-4f6a-8e07-2c9b4d6f1a83",
      "created_date": "2026-10-18T03:12:45.102351Z",
      "updated_date": "2026-10-18T03:12:45.102353Z",
      "namespace": null,
      "module_guid": null,
      "applet_guid": null,
      "acl_config": null,
      "acl_policy": null,
      "qty_open": null,
      "serial_no": null,
      "batch_no": null,
      "memberpoint_addon_json": null,
      "t2t_doc_hdr_guid": null,
      "t2t_doc_line_guid": null,
      "entity_line_guid": null,
      "point_type": null,
      "point_amount": null,
      "point_currency": null,
      "entity_fi_item_link_guid": null,
      "entity_inv_item_link_guid": null,
      "t2t_tenant_guid": null,
      "t2t_other_fi_item_guid": null,
      "t2t_other_fi_item_code": null,
      "coupon_hdr_guid": null,
      "coupon_line_guid": null,
      "item_txn_type": null,
      "item_sub_type": null,
      "guid_store_2": null,
      "arap_pns_amount": null,
      "arap_stlm_amount": null,
      "tracking_id": null,
      "uom": null,
      "unit_price_std": null,
      "bin_no": null,
      "unit_price_txn": 1499.00,
      "unit_price_net": null,
      "unit_price_std_by_uom": null,
      "unit_price_txn_by_uom": null,
      "uom_to_base_ratio": null,
      "entity_hdr_guid": null,
      "posting_fulfillment": null,
      "entity_json": null,
      "consignment_logic": null,
      "payment_channel_name": null,
      "parent_fi_item_guid": null,
      "einvoice_taxable_type_code": null,
      "einvoice_uom": null,
      "einvoice_uom_desc": null,
      "einvoice_taxable_type_desc": null,
      "einvoice_taxable_type_guid": null,
      "einvoice_item_classification_code": null,
      "einvoice_item_classification_desc": null,
      "cfg_production": null,
      "cfg_delivery": null,
      "del_region_hdr_guid": null,
      "del_region_hdr_reg_code": null,
      "del_region_hdr_state": null,
      "track_production_logic": null,
      "track_production_table": null,
      "track_production_guid": null,
      "track_production_id": null,
      "track_production_time_estimated": null,
      "track_production_time_actual": null,
      "track_production_time_planned": null,
      "track_production_pic_name": null,
      "track_production_pic_contact": null,
      "track_production_remarks": null,
      "track_production_status": null,
      "track_delivery_logic": null,
      "track_delivery_table": null,
      "track_delivery_guid": null,
      "track_delivery_id": null,
      "track_delivery_time_estimated": null,
      "track_delivery_time_actual": null,
      "track_delivery_time_planned": null,
      "track_delivery_pic_name": null,
      "track_delivery_pic_contact": null,
      "track_delivery_remarks": null,
      "track_delivery_status": null,
      "packing_status": null,
      "point_start_date": null,
      "point_end_date": null,
      "multi_level_disc_json": null,
      "posting_bundle": null,
      "delivery_required": null,
      "delivery_option": null,
      "membership_ext_start_date": null,
      "ready_to_ship": null,
      "track_delivery_date_requested": null,
      "finance_charge_amount": null,
      "cost_ma_amount": null,
      "cost_ma_price": null,
      "cost_wa_amount": null,
      "cost_wa_price": null,
      "cost_fifo_amount": null,
      "cost_fifo_price": null,
      "cost_lifo_amount": null,
      "cost_lifo_price": null,
      "cost_manual_amount": null,
      "cost_manual_price": null,
      "cost_replacement_amount": null,
      "cost_replacement_price": null,
      "cost_last_purchase_location": null,
      "cost_last_purchase_company": null,
      "cost_ma_amount_company": null,
      "cost_ma_price_company": null,
      "cost_wa_amount_company": null,
      "cost_wa_price_company": null,
      "cost_fifo_amount_company": null,
      "cost_fifo_price_company": null,
      "cost_fifo_price_per_txn_company": null,
      "cost_fifo_amount_per_txn_company": null,
      "cost_lifo_amount_company": null,
      "cost_lifo_price_company": null,
      "cost_manual_amount_company": null,
      "cost_manual_price_company": null,
      "cost_replacement_amount_company": null,
      "cost_replacement_price_company": null,
      "landed_txn_amount": null,
      "landed_txn_price": null,
      "landed_cost_ma_price": null,
      "landed_cost_ma_amount": null,
      "landed_cost_wa_amount": null,
      "landed_cost_wa_price": null,
      "landed_cost_fifo_amount": null,
      "landed_cost_fifo_price": null,
      "landed_cost_lifo_amount": null,
      "landed_cost_lifo_price": null,
      "landed_cost_ma_price_company": null,
      "landed_cost_ma_amount_company": null,
      "landed_cost_wa_amount_company": null,
      "landed_cost_wa_price_company": null,
      "landed_cost_fifo_amount_company": null,
      "landed_cost_fifo_price_company": null,
      "landed_cost_lifo_amount_company": null,
      "landed_cost_lifo_price_company": null,
      "posting_mto": null,
      "delivery_process_hdr_guid": null,
      "delivery_process_status_guid": null,
      "delivery_process_status_name": null,
      "delivery_process_resolution_guid": null,
      "delivery_process_resolution_code": null,
      "pickup_branch_guid": null,
      "pick_pack_queue": false,
      "delivery_branch_guid": null,
      "delivery_branch_code": null,
      "delivery_location_guid": null,
      "delivery_location_code": null,
      "wf_process_hdr_guid": null,
      "wf_process_status_guid": null,
      "wf_process_status_code": null,
      "wf_resolution_guid": null,
      "wf_resolution_code": null,
      "tax_tariff_code": null,
      "tax_industry_code": null,
      "line_bundle_guid": null,
      "apt_weight_disc": null,
      "apt_amt_discount": null,
      "apt_amount_txn": null,
      "unit_price_std_with_tax": null,
      "unit_discount": null,
      "unit_price_std_by_uom_with_tax": null,
      "unit_discount_with_tax": null,
      "replacement_unit_price_excl_tax": 0.0,
      "ref_price1_excl_tax": 0.0,
      "ref_price2_excl_tax": 0.0,
      "ref_price3_excl_tax": 0.0,
      "delta_price1_excl_tax": 0.0,
      "delta_price2_excl_tax": 0.0,
      "delta_price3_excl_tax": 0.0,
      "rebate_price1_excl_tax": 0.0,
      "rebate_price2_excl_tax": 0.0,
      "rebate_price3_excl_tax": 0.0,
      "sales_min_price_excl_tax": 0.0,
      "sales_max_price_excl_tax": 0.0,
      "purchase_min_price_excl_tax": 0.0,
      "purchase_max_price_excl_tax": 0.0,
      "purchase_selling_list_price_incl_tax": null,
      "sales_promotion_price_excl_tax": null,
      "is_synced": false,
      "discount_detail_method": null,
      "discount_detail_value": 0.0,
      "discount_detail_promo_code": null,
      "pgw_merchant_ref_no": null,
      "pgw_txn_hdr_guid": null,
      "pgw_txn_status": null,
      "display_option": null,
      "coupon_line_guid_json": null,
      "xtn_process_hdr_guid": null,
      "xtn_process_status_guid": null,
      "xtn_process_resolution_guid": null,
      "xtn_workflow_updated_at": null,
      "shipping_fee_courier_price": null,
      "shipping_fee_onsite_price": null,
      "shipping_fee_self_pickup_price": null,
      "installation_fee": null,
      "nsti_stock_hdr_guid": null,
      "vehicle_hdr_guid": null,
      "vehicle_registration_no": null,
      "pet_hdr_guid": null,
      "pet_id": null,
      "pet_name": null,
      "svc_record_01": null,
      "svc_record_02": null,
      "svc_issue_hdr_guid": null,
      "settlement_type": null,
      "pricing_scheme_hdr_guid": null,
      "session_guid": null,
      "votebook_guid": null,
      "budget_item_guid": null,
      "budget_register_guid": null,
      "budget_fiscal_year_guid": null,
      "budget_fiscal_period_guid": null,
      "posting_budget": null,
      "trade_tariff_code": null,
      "trade_country_of_origin": null,
      "tax_exemption_details": null,
      "tax_exemption_amount": null,
      "discount_description": null,
      "forex_source_hdr_guid": null,
      "forex_source_history_guid": null,
      "segment_code": null,
      "project_code": null,
      "profit_center_code": null,
      "gl_dimension_code": null,
      "mrp_request_status_production": null,
      "mrp_request_status_schedule": null,
      "mrp_request_status_exists": null,
      "mrp_request_remarks": null,
      "after_final_step_json": null,
      "forex_doc_line_guid": null,
      "container_inv_item_guid": null,
      "job_order_qty": 0.0,
      "mrp_produced_qty": 0.0,
      "packing_order_qty": 0.0,
      "file_batch_id": null,
      "client_doc_1": null,
      "client_doc_2": null,
      "client_doc_3": null,
      "client_doc_4": null,
      "client_doc_5": null,
      "inv_item_guid": null,
      "reference_date": null,
      "revision_history_json": null,
      "txn_group_01_guid": null,
      "txn_group_02_guid": null,
      "parent_fi_item_code": null,
      "parent_fi_item_name": null,
      "delta_price1_percentage": null,
      "delta_price2_percentage": null,
      "delta_price3_percentage": null,
      "item_point_rate": null,
      "item_point_amount": null,
      "cashbook_guid": null,
      "report_unit_replacement_cost": null,
      "report_unit_manual_cost": null,
      "report_unit_commission": null,
      "report_unit_rebate": null,
      "custom_field_json": {},
      "cash_topup_price": null,
      "pricebook_hdr_guid": null,
      "pricebook_line_guid": null
    }
  ],
  "bl_fi_generic_doc_budget_sub_line": []
}

Always POST. Always application/json. The method is not configurable.

The body is the record itself, in the same container shape the Data API returns — and nothing else.

There is no envelope. The payload carries no topic name, no event identifier, no timestamp, no tenant code and no attempt number. Your endpoint knows which event it is only from which URL it was configured on, so give every subscription its own path. And do not assume the body is always an object: several delete topics send the JSON literal true, and a couple send an English sentence with Content-Type: application/json. Parse defensively.

Your endpoint should return quickly — acknowledge, queue, and process asynchronously. BigLedger allows 60 seconds to establish the connection and then waits indefinitely for your response, and delivery for the whole platform runs on a small shared pool. An endpoint that accepts a connection and then hangs is worse than one that fails fast.

Topics

POST /core2/tnt/dm/webhook-topic/populate-default seeds 54 topics. The ones most integrations use:

AreaTopics
EntitiesCUSTOMER_CREATED · CUSTOMER_UPDATED · CUSTOMER_DELETED · SUPPLIER_CREATED · SUPPLIER_UPDATED · SUPPLIER_DELETED · EMPLOYEE_CREATED · EMPLOYEE_UPDATED · EMPLOYEE_DELETED
ItemsFINANCIAL_ITEM_CREATED · FINANCIAL_ITEM_UPDATED · FINANCIAL_ITEM_DELETED · INVENTORY_UPDATED
Sales documentsINTERNAL_SALES_ORDER_CREATED · INTERNAL_SALES_ORDER_UPDATED · INTERNAL_SALES_ORDER_DELETED · INTERNAL_SALES_INVOICE_CREATED · INTERNAL_SALES_RETURN_CREATED · INTERNAL_SALES_RETURN_UPDATED · INTERNAL_SALES_RETURN_DELETED
OrganisationCOMPANY_CREATED · COMPANY_UPDATED · BRANCH_CREATED · BRANCH_UPDATED · LOCATION_CREATED · LOCATION_UPDATED · LOCATION_DELETED
MembershipMEMBERSHIP_CARD_CREATED · MEMBERSHIP_CARD_UPDATED · MEMBERSHIP_CARD_DELETED · MEMBERSHIP_CLASS_CREATED · MEMBERSHIP_POINTS_TXN_CREATED
OtherVOUCHER_CREATED · VOUCHER_UPDATED · VOUCHER_DELETED · INQUIRY_CREATED · INQUIRY_UPDATED · INQUIRY_DELETED · PAYMENT_CONFIG_CREATED · PAYMENT_CONFIG_UPDATED · PAYMENT_CONFIG_DELETED

Some seeded topics never fire, because the code path they name does not emit them — the supplier sales-order and supplier sales-return topics are the clearest example. And document topics follow the pattern <SERVER_DOC_TYPE>_CREATED / _UPDATED / _DELETED, so document types beyond the seeded handful do emit events; their topic records just have to be created by hand.

Before you build on a topic, test it. Subscribe, make the change in the tenant, and confirm your endpoint is called. A topic that is registered but never fires looks identical to a quiet system.

Related documentation

Last updated on