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.
- Find the topic record’s
guidwithGET /core2/tnt/dm/webhook-topic/query, from a row whosestatusisACTIVE. If the list is empty, seed it once withPOST /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). - 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). - 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
POSTcarrying the header name and value you set (What success looks like). - Re-read each record by its
guidbefore acting on it, log on your side, and add the reconciliation pull onupdated_date_frombefore 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.
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.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 throughGET …/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 thenPOSTed to your URL with anX-BLG-Eventheader 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 a2xxreply whose JSON body carries"code": 10000; any other2xxreply 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 bare200.
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": ""
}| Field | Notes |
|---|---|
topic_hdr_guid | The topic record’s GUID, not the topic code |
url | Maximum 255 characters. Not validated — a typo produces a subscription that silently never delivers. |
auth_header_name | A valid HTTP header name. No spaces. |
auth_header_value | The shared secret. Maximum 255 characters. |
status | ACTIVE. 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). |
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:
| Create | POST /core2/tnt/dm/webhook-subscription |
| Update | PUT /core2/tnt/dm/webhook-subscription |
| Delete | DELETE /core2/tnt/dm/webhook-subscription/{guid} |
| List | GET /core2/tnt/dm/webhook-subscription |
| Read one | GET /core2/tnt/dm/webhook-subscription/{guid} |
| Filter | GET /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.
- Point the subscription at a request-capture service (
https://webhook.site/…or your ownnc -l 8080) instead of your real endpoint. - Make the change the topic names — for
CUSTOMER_CREATED, create one customer in the tenant. - 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 asbl_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:
- 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 exactlyACTIVE, whatever the subscription says. Nothing makestopic_codeunique, and the lookup takes the first record it finds, so a secondPOSTof 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. - The subscription is not
DELETED. Each create, update and delete leaves a change record with its date, not who made it. - The event really happened. A sales invoice sends nothing until it is FINAL; other document
types send
_CREATEDwhen the draft is saved (What arrives). - Your receiver still accepts the header. The
POSTcarries only the header stored on the subscription. If your side rotated that secret and nobodyPUTthe new value onto the subscription, every delivery is refused with a401, recorded, and never retried. Revoking the BigLedger API key your integration uses does not stop deliveries; it stops your reconciliation pull. - 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
200with 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:
- Subscribe to the topics you care about, one URL each.
- 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. - Reconcile on a schedule with an
updated_date_frompull. This catches everything the webhook dropped, and it is the part you must not skip. - Log every delivery on your side, with the raw body. The platform’s own log is not readable.
- Return
200fast, 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
OKBigLedger 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.
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:
| Area | Topics |
|---|---|
| Entities | CUSTOMER_CREATED · CUSTOMER_UPDATED · CUSTOMER_DELETED · SUPPLIER_CREATED · SUPPLIER_UPDATED · SUPPLIER_DELETED · EMPLOYEE_CREATED · EMPLOYEE_UPDATED · EMPLOYEE_DELETED |
| Items | FINANCIAL_ITEM_CREATED · FINANCIAL_ITEM_UPDATED · FINANCIAL_ITEM_DELETED · INVENTORY_UPDATED |
| Sales documents | INTERNAL_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 |
| Organisation | COMPANY_CREATED · COMPANY_UPDATED · BRANCH_CREATED · BRANCH_UPDATED · LOCATION_CREATED · LOCATION_UPDATED · LOCATION_DELETED |
| Membership | MEMBERSHIP_CARD_CREATED · MEMBERSHIP_CARD_UPDATED · MEMBERSHIP_CARD_DELETED · MEMBERSHIP_CLASS_CREATED · MEMBERSHIP_POINTS_TXN_CREATED |
| Other | VOUCHER_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.