Purchase Invoice
Purchase invoices are one of the document types served by BigLedger’s shared financial-document API. The document type is a path segment, and for purchase invoices it is internal-purchase-invoices.
Base path: https://api-etl.akaun.com/core2/tnt/dm/erp/gen-doc
Path segment ({docType}): internal-purchase-invoices
server_doc_type field value: INTERNAL_PURCHASE_INVOICE
See ERP Core API for authentication, the response envelope, and shared query parameters.
The path segment is not the same string as the server_doc_type field. The URL takes the lower-case, hyphenated, plural form (internal-purchase-invoices); the server_doc_type field inside the document takes the upper-case singular constant (INTERNAL_PURCHASE_INVOICE).
Using the upper-case form in the URL returns 404, which can read as “no such document” rather than “wrong path”. The response body varies by endpoint: query and get-by-guid return an empty body, while create and delete return {"code": "OK_NODATA", ...}. If a request 404s unexpectedly, check the path segment first.
Note also that GET /etl-ep/server-doc-types lists the field values, not the path segments, so it is not the list to copy URLs from. The table below has the mapping for the common types.
Everything on this page applies unchanged to payment vouchers and to every other document type: swap the {docType} segment.
Path segment ({docType}) | server_doc_type field value |
|---|---|
internal-purchase-invoices | INTERNAL_PURCHASE_INVOICE |
internal-payment-vouchers | INTERNAL_PAYMENT_VOUCHER |
internal-reimbursement-payment-vouchers | INTERNAL_REIMBURSEMENT_PAYMENT_VOUCHER |
internal-purchase-orders | INTERNAL_PURCHASE_ORDER |
internal-purchase-returns | INTERNAL_PURCHASE_RETURN |
internal-purchase-credit-notes | INTERNAL_PURCHASE_CREDIT_NOTE |
internal-purchase-debit-notes | INTERNAL_PURCHASE_DEBIT_NOTE |
internal-purchase-goods-received-notes | INTERNAL_PURCHASE_GOODS_RECEIVED_NOTE |
internal-receipt-vouchers | INTERNAL_RECEIPT_VOUCHER |
internal-sales-invoices | INTERNAL_SALES_INVOICE |
A handful of older types use underscores rather than hyphens in the path segment, for example internal_purchase_grn_stock_in. If a hyphenated guess returns 404, try the underscore form before assuming the endpoint is missing.
Permissions
| Operation | Permission |
|---|---|
| Create | TNT_API_DOC_INTERNAL_PURCHASE_INVOICE_CREATE_TGT_GUID |
| Read | TNT_API_DOC_INTERNAL_PURCHASE_INVOICE_READ_TGT_GUID |
| Update | TNT_API_DOC_INTERNAL_PURCHASE_INVOICE_UPDATE_TGT_GUID |
| Delete | TNT_API_DOC_INTERNAL_PURCHASE_INVOICE_DELETE_TGT_GUID |
_TGT_GUID suffix means the permission is granted against specific company, branch and store GUIDs, and is checked against the guid_comp, guid_branch, guid_store, guid_store_2 and delivery_branch_guid values on the document you are sending. A key whose owner holds the permission for one company cannot create invoices for another, and that is returned as a 403. If you get one, check the company and store GUIDs on the payload as well as the key’s permissions.Endpoints
All of these already accept an access key. Substitute internal-purchase-invoices for {docType}.
Core CRUD
| Operation | Path (relative to the base path) |
|---|---|
| Create | POST /{docType}/etl-ep |
| Create several | POST /{docType}/etl-ep/multi |
| Update | PUT /{docType}/etl-ep |
| Delete | DELETE /{docType}/etl-ep/{guid} |
| List all | GET /{docType}/etl-ep |
| Get one | GET /{docType}/etl-ep/{guid} |
| Query | GET /{docType}/etl-ep/query |
| Query with a POST body | POST /{docType}/etl-ep/query |
| Query across all doc types | GET /etl-ep/query |
| List valid document types | GET /etl-ep/server-doc-types |
Lifecycle
| Operation | Path |
|---|---|
| Set posting status | PUT /{docType}/etl-ep/update-posting-status/{guid} |
| Clear posting status | PUT /{docType}/set-posting-status-null/etl-ep/{guid} |
| Void | PUT /{docType}/etl-ep/void/{guid} |
| Discard | PUT /{docType}/discard/etl-ep/{guid} |
| Purge | DELETE /{docType}/purge/etl-ep/{guid} |
| Multi-document operation | POST /etl-ep/multi-doc |
Reporting and files
| Operation | Path |
|---|---|
| Query snapshot | GET /{docType}/etl-ep/query/snapshot |
| Query snapshot with a POST body | POST /{docType}/etl-ep/query/snapshot |
| Query by transaction type | GET /{docType}/etl-ep/type/query |
| Print to PDF | GET /{docType}/etl-ep/print-jasper-pdf/{guid} |
| Batch print | GET /{docType}/etl-ep/batch-print-jasper-pdf/{guids} |
| Batch print with a POST body | POST /{docType}/etl-ep/batch-print-jasper-pdf |
| Attach a file | PUT /{docType}/etl-ep/add-files/{hdrGuid} |
| Replace a file | PUT /{docType}/etl-ep/replace-file/{extGuid} |
| Get a file | GET /{docType}/etl-ep/file/{extGuid} |
| Delete a file | DELETE /{docType}/etl-ep/file/{extGuid} |
| Unbundle | GET /{docType}/bundle/unwrapped/etl-ep/{guid} |
Standard ETL endpoints
Higher-throughput variants intended for bulk import:
| Operation | Path |
|---|---|
| Create (standard) | POST /standard/{docType}/etl-ep |
| Create, returning only the GUID | POST /custom-prev/standard/{docType}/etl-ep |
| Create via JSON import (async) | POST /custom/standard/{docType}/etl-ep |
| Check JSON import status | GET /custom/standard/{docType}/etl-ep/status/{guid} |
The GenericDocumentContainer
{
"bl_fi_generic_doc_hdr": { },
"bl_fi_generic_doc_line": [ ],
"bl_fi_generic_doc_ext": [ ],
"bl_fi_generic_doc_link": [ ],
"bl_fi_generic_doc_event": [ ],
"bl_fi_generic_doc_budget_sub_line": [ ]
}bl_fi_generic_doc_hdr and bl_fi_generic_doc_line are the two you need. The rest may be omitted or sent as [].
bl_fi_generic_doc_hdr: fields you are likely to need
The fields a purchase invoice uses.
| Field | Type | On create | Description |
|---|---|---|---|
guid | UUID | Optional | Generated if omitted. Required on update. |
guid_comp | UUID | Required | Company the invoice belongs to. Also a permission target. |
guid_store | UUID | Required | Store/location. Also a permission target. |
guid_branch | UUID | Optional | Branch. Also a permission target. |
doc_entity_hdr_guid | UUID | Required | The supplier being invoiced. |
server_doc_type | string | Required | INTERNAL_PURCHASE_INVOICE, the upper-case constant. Required on the header: it is not inferred from the {docType} path segment, and omitting it fails with GENERIC_DOC_HDR_INVALID_SERVERDOCTYPE. Lines inherit it from the header. |
amount_signum | decimal | Required | -1 on a purchase invoice. A purchase invoice is money owed, so the sign is negative. Validated on the header only; sending 1 fails with GENERIC_DOC_INVALID_SIGNUM. |
client_doc_type | string | Recommended | Your classification of the document. Often the same as server_doc_type, but not always: an invoice raised from a goods-received note carries INTERNAL_PURCHASE_GOODS_RECEIVED_NOTE. |
doc_source_type | string | Recommended | Where the document came from. Use INTERNAL for documents you create. |
doc_ccy | string | Recommended | Document currency, e.g. MYR. |
date_txn | datetime | Recommended | Transaction date. Defaults to now. |
due_date | datetime | Optional | Payment due date. |
doc_reference | string | Optional | The supplier’s invoice number. |
doc_desc | string | Optional | Short description. |
doc_remarks | string | Optional | Free-text remarks. |
doc_reference_tax_num | string | Optional | Tax invoice number. |
doc_reference_tax_date | datetime | Optional | Tax invoice date. |
amount_std | decimal | Recommended | Gross amount before discount. |
amount_discount | decimal | Optional | Total discount. |
amount_net | decimal | Recommended | Net amount after discount, before tax. |
amount_tax_gst | decimal | Optional | GST/SST amount. |
amount_tax_wht | decimal | Optional | Withholding tax amount. |
amount_txn | decimal | Recommended | Total payable. |
amount_open_balance | decimal | Optional | Outstanding balance. Maintained by the system as payments settle. |
foreign_ccy | boolean | Optional | Set when the document is in a foreign currency. |
base_doc_ccy | string | Optional | Base currency for a foreign-currency document. |
base_doc_xrate | decimal | Optional | Exchange rate to the base currency. |
posting_status | string | Optional | DRAFT or FINAL. See the lifecycle section. |
status | string | Optional | Defaults to ACTIVE. |
revision | UUID | Optional | Generated if omitted. Send the value you read back when updating. |
client_key | string | Optional | Your system’s key for this document. |
client_source | string | Optional | Your system’s name. |
client_doc_1 … client_doc_5 | string | Optional | Your own document references. |
client_value | string | Optional | Free-form value for your own use. |
property_json | object | Optional | Arbitrary extra properties. |
credit_terms_json | object | Optional | Credit terms. |
billing_json | object | Optional | Billing details. |
server_doc_1 … server_doc_5 | string | Server-assigned | Running numbers generated by BigLedger. server_doc_1 is the document number you will recognise, and it stays null while the document is DRAFT: numbers are assigned when it is finalized. |
code_company, code_branch, code_location | string | Server-maintained | Code equivalents of the company, branch and store GUIDs. Useful for filtering; do not rely on setting them. |
arap_pns_amount, arap_stlm_amount, arap_doc_open, arap_bal, arap_contra | decimal | Server-maintained | AR/AP settlement tracking, updated as payment vouchers settle the invoice. |
amount_internal_settlement | decimal | Server-maintained | Amount already applied against this invoice. |
doc_entity_hdr_json, delivery_entity_json | object | Server-maintained | Snapshots of the supplier and delivery entity at the time the document was written. |
posting_inventory, posting_journal, posting_bundle | string | Server-maintained | Whether each posting step has run. |
pin_code, pin_type, pin_hash, pin_algorithm | string | Server-assigned | Generated on create when no PIN is supplied. Do not set these. |
created_date, updated_date | datetime | Server-assigned | |
created_by_subject_guid, updated_by_subject_guid | UUID | Server-assigned | Set from the access key’s owner. |
Create fails with GENERIC_DOC_HDR_GUID_COMP_IS_NULL_OR_EMPTY or GENERIC_DOC_HDR_GUID_STORE_IS_NULL_OR_EMPTY when guid_comp or guid_store is missing.
bl_fi_generic_doc_line
| Field | Type | On create | Description |
|---|---|---|---|
guid | UUID | Optional | Generated if omitted. |
generic_doc_hdr_guid | UUID | Set for you | Filled in from the header. |
item_guid | UUID | Required | The item being purchased. |
item_code | string | Required | The item’s code. |
quantity_base | decimal | Required | Quantity in the item’s base unit. |
amount_std | decimal | Required | Gross line amount before discount. |
server_doc_type | string | Server-filled | Inherited from the header. |
amount_signum | decimal | Server-filled | Forced to -1. Anything you send is discarded. |
quantity_signum | decimal | Server-filled | Forced to 1. Anything you send is discarded. |
item_name | string | Recommended | Item name as it should appear on the document. |
item_desc | string | Optional | Item description. |
item_remarks | string | Optional | Line remarks. |
uom | string | Optional | Unit of measure. |
qty_by_uom | decimal | Optional | Quantity in that unit. |
uom_to_base_ratio | decimal | Optional | Conversion factor to the base unit. |
unit_price_by_uom | decimal | Optional | Unit price in that unit. |
amount_discount | decimal | Optional | Line discount. |
amount_net | decimal | Recommended | Line net after discount, before tax. |
amount_txn | decimal | Recommended | Line total. |
tax_gst_code, tax_gst_type, tax_gst_rate, amount_tax_gst | string / decimal | Optional | GST/SST on the line. |
tax_wht_code, tax_wht_type, tax_wht_rate, amount_tax_wht | string / decimal | Optional | Withholding tax on the line. |
guid_glcode | UUID | Optional | GL code to post this line to. |
guid_comp, guid_branch, guid_store | UUID | Optional | Default to the header’s values. |
doc_ccy | string | Optional | Line currency. Defaults to the header’s. |
status | string | Optional | Defaults to ACTIVE. |
revision | UUID | Optional | Generated if omitted. |
Create fails with GENERIC_DOC_LINE_OBJECT_ITEM_GUID_IS_NULL_OR_EMPTY, ..._ITEM_CODE_IS_NULL_OR_EMPTY, ..._QUANTITY_BASE_IS_NULL_OR_EMPTY or ..._AMOUNT_STD_IS_NULL_OR_EMPTY when a required line field is missing.
Document lifecycle
posting_status controls whether the document has hit the ledgers:
| Value | Meaning |
|---|---|
DRAFT | Created but not posted. Freely editable. |
FINAL | Posted. Journals, tax and AP balances are written. |
Create the document as DRAFT, check it, then move it to FINAL. Once final, use void rather than update or delete: voiding writes the reversing entries, while deleting does not.
| Action | What it does |
|---|---|
| Discard | Marks the document discarded. Reversible bookkeeping-wise; the record stays. |
| Void | Reverses a finalized document. This is the correct way to cancel a posted invoice. |
| Delete | Removes the document. Only appropriate for a draft that was never posted. |
| Purge | Permanently removes the record and its history. |
Create a purchase invoice
POST /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-epcurl -X POST "https://api-etl.akaun.com/core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep" \
-H "AccessId: YOUR_ACCESS_ID" \
-H "AccessKey: YOUR_ACCESS_KEY" \
-H "tenantCode: YOUR_TENANT_CODE" \
-H "Content-Type: application/json" \
-d '{
"bl_fi_generic_doc_hdr": {
"server_doc_type": "INTERNAL_PURCHASE_INVOICE",
"guid_comp": "3f2a7c10-9b4e-4d61-8a22-1c5e7f9d0b33",
"guid_store": "6b8d1f42-7e35-4c09-ad81-1d4e8b2c5a70",
"doc_entity_hdr_guid": "e7b2d48f-1c93-4a05-8e67-4f9a3b1d5c26",
"doc_ccy": "MYR",
"date_txn": "2026-09-14T00:00:00+08:00",
"due_date": "2026-10-14T00:00:00+08:00",
"doc_reference": "INV-2026-00187",
"doc_desc": "Office stationery, September",
"amount_std": 1000.00,
"amount_discount": 0.00,
"amount_net": 1000.00,
"amount_tax_gst": 60.00,
"amount_txn": 1060.00,
"amount_signum": -1,
"posting_status": "DRAFT",
"status": "ACTIVE"
},
"bl_fi_generic_doc_line": [
{
"item_guid": "9c4a7e21-5d83-4b16-8f52-3a6e1c9b7d40",
"item_code": "STN-A4-80",
"item_name": "A4 Paper 80gsm, ream",
"quantity_base": 40,
"uom": "REAM",
"qty_by_uom": 40,
"uom_to_base_ratio": 1,
"unit_price_by_uom": 25.00,
"amount_std": 1000.00,
"amount_discount": 0.00,
"amount_net": 1000.00,
"tax_gst_code": "SR",
"tax_gst_rate": 6,
"amount_tax_gst": 60.00,
"amount_txn": 1060.00
}
],
"bl_fi_generic_doc_ext": [],
"bl_fi_generic_doc_link": [],
"bl_fi_generic_doc_event": []
}'Response: 200 OK with the created document. server_doc_1 carries the running number BigLedger assigned:
{
"code": "OK_RESPONSE",
"data": {
"bl_fi_generic_doc_hdr": {
"guid": "4d8b2f61-3a07-4e95-b12c-7f6a9d3e5c84",
"server_doc_type": "INTERNAL_PURCHASE_INVOICE",
"server_doc_1": "PI-2026-000042",
"guid_comp": "3f2a7c10-9b4e-4d61-8a22-1c5e7f9d0b33",
"doc_entity_hdr_guid": "e7b2d48f-1c93-4a05-8e67-4f9a3b1d5c26",
"doc_reference": "INV-2026-00187",
"doc_ccy": "MYR",
"amount_txn": 1060.00,
"amount_open_balance": 1060.00,
"posting_status": "DRAFT",
"status": "ACTIVE",
"revision": "1e5c9a37-8b40-4d26-9f73-2a8e6c4b1d95",
"created_date": "2026-09-14T09:30:00+08:00"
},
"bl_fi_generic_doc_line": [ ]
},
"message": ""
}Keep the returned guid: payment vouchers settle against it.
Creating several at once
POST /{docType}/etl-ep/multi takes a JSON array of containers and creates them in one call. Use it for bulk import rather than looping over the single-create endpoint.
Update a purchase invoice
PUT /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-epSend the whole container with guid and revision set. Only update documents still in DRAFT. Void and re-issue a finalized one.
Finalize a purchase invoice
PUT /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/update-posting-status/{guid}The body is a small JSON object, not the whole document:
| Field | Type | Description |
|---|---|---|
posting_status | string | DRAFT or FINAL. |
validate_serial_signum_zero | boolean string | Optional. "true" to validate serial-number balance on posting. Defaults to false. |
curl -X PUT "https://api-etl.akaun.com/core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/update-posting-status/4d8b2f61-3a07-4e95-b12c-7f6a9d3e5c84" \
-H "AccessId: YOUR_ACCESS_ID" \
-H "AccessKey: YOUR_ACCESS_KEY" \
-H "tenantCode: YOUR_TENANT_CODE" \
-H "Content-Type: application/json" \
-d '{"posting_status": "FINAL"}'Void a purchase invoice
PUT /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/void/{guid}Takes the same small JSON body as the posting-status endpoint.
curl -X PUT "https://api-etl.akaun.com/core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/void/4d8b2f61-3a07-4e95-b12c-7f6a9d3e5c84" \
-H "AccessId: YOUR_ACCESS_ID" \
-H "AccessKey: YOUR_ACCESS_KEY" \
-H "tenantCode: YOUR_TENANT_CODE" \
-H "Content-Type: application/json" \
-d '{}'Delete, discard and purge
DELETE /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/{guid}
PUT /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/discard/etl-ep/{guid}
DELETE /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/purge/etl-ep/{guid}See the lifecycle table above for which one to reach for.
Get one purchase invoice
GET /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/{guid}Query purchase invoices
GET /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/queryThe same criteria can be sent as a JSON body to POST /{docType}/etl-ep/query when the filter is too long for a URL.
In addition to the shared query parameters:
| Parameter | Type | Description |
|---|---|---|
has_line | boolean | Include line rows in the response. Set this to true or you get headers only. |
has_ext | boolean | Include extension rows. |
has_link | boolean | Include document links. |
has_event | boolean | Include events. |
has_attachment | boolean | Include attachments. |
guid_comp | UUID | Filter by company. |
guid_branch | UUID | Filter by branch. |
guid_store | string (UUID) | Filter by store. |
doc_entity_hdr_guid | UUID | Filter by supplier. |
server_doc_1 | string | Exact BigLedger document number. |
client_doc_1 | string | Exact client document number. |
client_doc_1_like | string | Partial client document number. |
client_doc_1s | string set | Several client document numbers. Repeat the parameter. |
client_doc_2 | string | Second client reference. |
client_doc_type | string | Client document type. |
client_doc_types | string set | Several client document types. Repeat the parameter. |
doc_source_type | string | Document source. |
doc_desc | string | Match on description. |
hdr_doc_ccy | string | Filter by currency. |
hdr_posting_status | string | DRAFT or FINAL. |
arap_bal_range | string | Filter by outstanding balance range. |
hdr_client_key, hdr_client_source | string | Filter by your system’s identifiers. |
code_company, code_branch, code_location | string | Filter by code instead of GUID. |
contact_hdr_guid, contact_key_guid, member_guid | UUID | Contact and member filters. |
file_batch_id | string | Filter by import batch. |
curl -G "https://api-etl.akaun.com/core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/query" \
-H "AccessId: YOUR_ACCESS_ID" \
-H "AccessKey: YOUR_ACCESS_KEY" \
-H "tenantCode: YOUR_TENANT_CODE" \
--data-urlencode "doc_entity_hdr_guid=e7b2d48f-1c93-4a05-8e67-4f9a3b1d5c26" \
--data-urlencode "hdr_posting_status=FINAL" \
--data-urlencode "has_line=true" \
--data-urlencode "limit=50" \
--data-urlencode "calcTotalRecords=true"Print a purchase invoice
GET /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/print-jasper-pdf/{guid}Returns a PDF rather than the JSON envelope. GET .../etl-ep/batch-print-jasper-pdf/{guids} takes several GUIDs and returns a ZIP.