Audit Trail API
The audit trail records what changed somewhere else in BigLedger: which record, what it looked like before and after, who did it, and when. It is the data behind the Audit Trail screen in the Tenant Admin Applet.
The same record is served by two independent surfaces. Which one you want depends on whose data you are reading.
Base paths:
| Surface | Base path | Reads |
|---|---|---|
| Tenant | https://api.akaun.com/core2/tnt/dm/erp/audit-trails | One tenant’s own database. Send tenantCode. |
| Platform | https://api.akaun.com/core2/platform/dm/applet-audit-trail | The master database. Platform-wide, not tenant-scoped. |
See API Reference for authentication, and the Data API reference for the response envelope, streaming and paging.
/backoffice-ep, and the platform surface has no access-endpoint segment. For server-to-server work with an access key, see the integration path.Permissions
The caller needs one of these for each operation. The two surfaces have separate families with the same shape.
| Operation | Tenant permissions (any one) | Platform permissions (any one) |
|---|---|---|
| Create | TNT_API_DM_APPLET_AUDIT_TRAIL_OWNER, ..._ADMIN, ..._CREATE | MST_API_DM_APPLET_AUDIT_TRAIL_OWNER, ..._ADMIN, ..._CREATE |
| Read | ..._OWNER, ..._ADMIN, TNT_API_DM_APPLET_AUDIT_TRAIL_READ | ..._OWNER, ..._ADMIN, MST_API_DM_APPLET_AUDIT_TRAIL_READ |
| Update | ..._OWNER, ..._ADMIN, TNT_API_DM_APPLET_AUDIT_TRAIL_UPDATE | ..._OWNER, ..._ADMIN, MST_API_DM_APPLET_AUDIT_TRAIL_UPDATE |
| Delete | ..._OWNER, ..._ADMIN, TNT_API_DM_APPLET_AUDIT_TRAIL_DELETE | ..._OWNER, ..._ADMIN, MST_API_DM_APPLET_AUDIT_TRAIL_DELETE |
A platform administrator passes every check on the platform surface without holding any of its codes. A failed check answers CLIENT_AUTH_USER_NOT_AUTHORIZED, which is an authorisation problem rather than an authentication one.
Endpoints
| Operation | Tenant path | Platform path |
|---|---|---|
| Create | POST /backoffice-ep | POST / |
| Update | PUT /backoffice-ep | PUT / |
| Delete | DELETE /backoffice-ep/{guid} | DELETE /{guid} |
| List all | GET /backoffice-ep | GET / |
| Get one | GET /backoffice-ep/{guid} | GET /{guid} |
| Query | GET /backoffice-ep/query | GET /query |
Reads behave slightly differently on the two surfaces, which is worth knowing if you are building a listing.
- Tenant. Permission is checked once per request. A caller who passes the check sees every field of every record returned.
- Platform. Records outside the caller’s permission are returned with
"status": "PERMISSION_DENIED"and their remaining fields empty, rather than being omitted. This is a response-only marker, so do not send it back on a write.
The AppletAuditTrailContainer
The same shape on both surfaces, and the same shape a read returns.
{
"bl_applet_audit_trail": {
"guid": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed",
"applet_guid": "6f1c9e10-3a44-4f2e-9a71-0c3d6b2f77aa",
"applet_code": "internalSalesInvoiceApplet",
"action": "UPDATED",
"foreign_table": "bl_fi_generic_doc_hdr",
"foreign_key": "9c2a5f31-77b2-4d0a-8f4e-2b6a1d5c8e33",
"date_txn": "2026-09-15T10:04:22+08:00",
"message_json": { "payload": { } },
"status": "ACTIVE",
"revision": "0f3e7a52-1c6b-4a9d-8e21-7b4c9f0a1d26"
}
}PUT takes the whole record back, not a patch. A field you leave out is written as null.
bl_applet_audit_trail
| Field | Type | On create | Description |
|---|---|---|---|
guid | UUID | Optional | Generated if omitted. Required on update. |
applet_guid | UUID | Optional | The applet the change happened in. If supplied it must name an applet that exists and is not DELETED. |
applet_code | string | Optional | The applet’s registry code, for example internalSalesInvoiceApplet. Shown as Applet Code on the Audit Trail screen. |
action | string | Optional | What happened. BigLedger’s own writers use CREATED, UPDATED, DELETED or DISCARD. Shown as Action Name. Indexed. |
foreign_table | string | Optional | The table the audited record lives in, for example bl_fi_generic_doc_hdr. Shown as Table Name. Indexed. |
foreign_key | string | Optional | That record’s guid, as a string. Indexed. |
message_json | object | Optional | The before-and-after payload. See What writes rows. |
descr | string | Optional | Free text. BigLedger’s own writers put the action name here. |
date_txn | datetime | Optional | When the audited transaction happened, as distinct from when the record was written. Indexed. |
txn_type | string | Optional | Your transaction classification. |
id_user, id_device, id_user_agent | string | Optional | Your identifiers for who and what made the change. Each indexed. |
event_code | string | Optional | Your event classification. |
obj_type | string | Optional | Your object classification. |
link_guid | UUID | Optional | A free link to a related record. |
module_guid | UUID | Optional | Owning module. |
namespace | string | Optional | Namespace for multi-app tenants. |
vrsn | string | Optional | Version label. |
status | string | Optional | Defaults to ACTIVE. Every read excludes DELETED. |
revision | UUID | Optional | Generated if omitted. Send the value you read back when updating. |
created_date, updated_date | datetime | Server-assigned | Set on write. |
created_by_subject_guid | UUID | Server-assigned | Taken from your credential on create. On update, a value in your body is kept, and your credential fills it only when you leave it null. Must name a real login subject either way. |
updated_by_subject_guid | UUID | Server-assigned | Always set from the credential that made the write, so it reliably identifies the caller. |
created_by_name, updated_by_name | string | Server-assigned | Resolved by looking those subjects up, never read from your body. Create sets both; update refreshes updated_by_name only. Shown as Action By. |
property_json, acl_config, acl_policy | object | Optional | Carried, not interpreted. |
What writes rows
Most rows are not written by an API caller. BigLedger writes them itself when an audited operation runs, and those rows have a predictable shape.
actionanddescrboth carry the operation, one ofCREATED,UPDATED,DELETED,DISCARDforeign_tableandforeign_keyidentify the audited recordapplet_guidandapplet_codecome from the caller’s token, and are set only when that token carries an appletmessage_jsonis{"payload": { … }}, and the payload holds whichever ofold_data,new_dataanddelete_dataapplied, plus anauth_token_fieldsobject describing the caller
So a CREATED row carries new_data alone, an UPDATED row carries old_data and new_data so the two can be compared field by field, and a DELETED row carries delete_data, the removed record kept after the record itself is gone.
Querying
Both /query endpoints take the same parameters. The shared paging parameters first: limit (default 100), offset (default 0), orderBy (default the record’s guid), order (ASC or DESC) and calcTotalRecords, which must be true for totalRecords in the envelope to be anything but 0.
| Parameter | Matches |
|---|---|
applet_guid, applet_code | exactly |
foreign_table, foreign_key | exactly |
txn_type, event_code, obj_type | exactly |
id_user, id_device, id_user_agent | exactly |
created_by_subject_guid, updated_by_subject_guid | exactly |
created_by_name, updated_by_name | exactly, and case-sensitively |
link_guid | exactly |
status | exactly. Omit it and everything except DELETED comes back. |
date_txn_from, date_txn_to | an inclusive range on date_txn |
created_date_from, created_date_to | an inclusive range on created_date |
updated_date_from, updated_date_to | an inclusive range on updated_date |
Timestamps are ISO-8601 with an offset, as everywhere else in the API. Every filter above is an equality or a range, so supply whole values rather than fragments. To narrow by action, request the period you need and filter the results on your side. The Action By box on the Audit Trail screen maps to created_by_name, which is why it needs the actor’s profile name exactly as stored, not a login or an e-mail address.
Errors on write
POST and PUT validate before anything is stored, on both surfaces, and a failure returns HTTP 400 with the failing checks in the envelope. The five checks are the same for both methods.
code | Cause |
|---|---|
API_PLATFORM_DM_APPLET_AUDIT_TRAIL_OBJECT_GUID_IS_NULL | guid was null and could not be defaulted |
API_PLATFORM_DM_APPLET_AUDIT_TRAIL_OBJECT_STATUS_IS_NULL_OR_EMPTY | status was null |
API_PLATFORM_DM_APPLET_AUDIT_TRAIL_OBJECT_REVISION_IS_NULL | revision was null |
API_PLATFORM_DM_APPLET_AUDIT_TRAIL_OBJECT_APPLET_GUID_DOES_NOT_EXISTS | applet_guid was supplied but names no applet, or names a DELETED one |
API_PLATFORM_DM_APPLET_AUDIT_TRAIL_OBJECT_CREATED_BY_SUBJECT_GUID_DOES_NOT_EXIST | the subject on your credential is not a login subject |
DELETE returns CLIENT_VALIDATION_GUIDDOESNOTEXIST with HTTP 404 when no record carries that guid. The full list of code values is on the Error Codes page.
Scope
- Credentials. These endpoints are served for signed-in users. The server-to-server surface an access key reaches is described in the integration path and the Data API reference.
- Write access.
POST,PUTandDELETEare available to callers holding the matching permission in the table above. Grant those permissions deliberately, and keep read-only integrations on theREADpermission alone. - Coverage. Records are written for the operations that support auditing. If a search returns nothing, widen the date range and re-check
applet_codeandforeign_tablebefore drawing a conclusion. - Other audit trails. Claims, entities, journal lines, cashbook transaction lines, memberships and several other records keep their own audit trails. This page covers the applet audit trail only.
Related
- Tenant Admin Applet covers the Audit Trail screen this API serves: its listing columns, advanced search and detail view.
- Audit Trail guide walks the same screen through as an investigation, for a reader who is not calling the API.
- API Reference lists the API families, and which of them have a page.
- Authentication covers what may go in the
Authorizationheader, and where each credential works. - Error Codes lists every
codevalue the API can return.