Purchase Invoice
Purchase invoices are served by BigLedger’s shared financial-document API, with the document type as a path segment.
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
Authentication covers credentials, Data API the envelope and query parameters, and ERP Core API the shared errors and filters.
The fast path:
POST /internal-purchase-invoices/etl-epas aDRAFT, withserver_doc_type,amount_signum-1anddoc_source_typeINTERNALon the header (create).- Check it, then
PUT …/etl-ep/update-posting-status/{guid}with{"posting_status": "FINAL"}. Re-read before you void it; onRACE_CONDITION, wait and retry (finalize). - Cancel it with
PUT …/etl-ep/void/{guid}and{"posting_status": "VOID"}: an empty body voids nothing.
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 returns an empty body, while get-by-guid, create and delete return {"code": "OK_NODATA", ...}.
GET /etl-ep/server-doc-types lists the field values, not the path segments; the table below maps 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: that is a 403, so check the payload’s company and store GUIDs 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 | Required | Where the document came from. Use INTERNAL for documents you create. Leaving it out fails with GENERIC_DOC_HDR_INVALID_SOURCETYPE. |
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, client_source | string | Optional | Your system’s key for this document, and your system’s name. |
client_doc_1 … client_doc_5 | string | Optional | Whatever your own system calls this document. Kept beside BigLedger’s numbers, never instead of them — the generic document. |
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. server_doc_1 (tenant) is written just after the create, though the create response shows null; company, branch and location numbers come at finalise, or at create for a FINAL create. Plain integers, no prefix — the generic document. |
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 | object | Optional — filled from the supplier record when you omit it | The copy of the supplier the document carries. Send it and it is stored exactly as you send it; omit it and the server builds it from the supplier record as it is now. Never refreshed afterwards. See Send the supplier snapshot yourself. |
delivery_entity_json | object | Optional | The delivery entity’s details, copied onto the document in the same way. Nothing fills this one for you. |
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.
Send the supplier snapshot yourself
doc_entity_hdr_json is not a field the server owns. It is the copy of the supplier that the
document carries: BigLedger snapshots the supplier onto a document when it is issued instead of
reading through to the supplier record, so that a document opened years later still says what was
true when it was raised.
For an integration that matters in one specific way — whatever you send is kept, and whatever you omit is filled from the supplier record as it is now.
- Omit it and the server builds the block from
doc_entity_hdr_guidat the moment your call arrives:entityId(the supplier code),entityType,entityName,idNumber,identityType,email,phoneNumber,description,statusandcurrency— andcurrencyis hard-coded toMYR. The ETL create and update endpoints do this before the handler runs, and the data-consistency object does it on every other path. - Send it and it is stored exactly as you sent it and never refreshed. A later
PUTthat omits it on a document that already has one leaves it alone too, because the fill only runs on an empty block. The fill coversdoc_entity_hdr_jsononly:delivery_entity_jsonandbilling_jsonare stored as you send them or not at all.
If you are migrating historical documents, send the block. A 2022 invoice posted without one is stamped with the supplier’s 2026 name, e-mail and identity number, and nothing will tell you — the document looks complete and every value in it is plausible. Send what the supplier was in 2022 instead.
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 says whether the document is a draft or final:
| Value | Meaning |
|---|---|
DRAFT | Not final. Freely editable. |
FINAL | Final and numbered. The journal, tax and supplier balance follow in background jobs (Finalize). |
Create as DRAFT, check it, then finalise. A create sent as FINAL stores FINAL without Finalise’s checks or its posting chain, so it writes no journal; only the e-invoice job is queued for it. 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 has not been posted. |
| Purge | Permanently removes the record and its history. |
Create a purchase invoice
POST /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep
From here to Get one purchase invoice, each example uses the invoice the one before created: 50 USB-C cables from GadgetSphere’s accessory wholesaler, recorded, corrected, posted, then voided.
curl -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",
"doc_source_type": "INTERNAL",
"guid_comp": "3f2a7c10-9b4e-4d61-8a22-1c5e7f9d0b33",
"guid_branch": "8c1e4a27-5d39-4b62-9f08-2a7d6c3e1b54",
"guid_store": "6b8d1f42-7e35-4c09-ad81-1d4e8b2c5a70",
"doc_entity_hdr_guid": "e7b2d48f-1c93-4a05-8e67-4f9a3b1d5c26",
"doc_ccy": "MYR",
"date_txn": "2026-10-21T00:00:00+08:00",
"due_date": "2026-11-20T00:00:00+08:00",
"doc_reference": "INV-ACW-2611",
"doc_desc": "USB-C cables, October",
"client_source": "gadgetsphere-erp",
"client_key": "PI-2026-10-0187",
"amount_std": 600.00,
"amount_discount": 0.00,
"amount_net": 600.00,
"amount_txn": 600.00,
"amount_signum": -1,
"posting_status": "DRAFT",
"status": "ACTIVE"
},
"bl_fi_generic_doc_line": [
{
"item_guid": "9c4a7e21-5d83-4b16-8f52-3a6e1c9b7d40",
"item_code": "GS-AC-CABLE-C-1M",
"item_name": "USB-C cable 1m",
"txn_type": "PNS",
"quantity_base": 50,
"uom": "UNIT",
"qty_by_uom": 50,
"uom_to_base_ratio": 1,
"unit_price_by_uom": 12.00,
"amount_std": 600.00,
"amount_discount": 0.00,
"amount_net": 600.00,
"amount_txn": 600.00
}
],
"bl_fi_generic_doc_ext": [],
"bl_fi_generic_doc_link": [],
"bl_fi_generic_doc_event": []
}'Response: 200 OK with the created document, every column of it. Shortened here:
{
"code": "OK_RESPONSE",
"data": {
"bl_fi_generic_doc_hdr": {
"guid": "4d8b2f61-3a07-4e95-b12c-7f6a9d3e5c84",
"server_doc_type": "INTERNAL_PURCHASE_INVOICE",
"doc_source_type": "INTERNAL",
"server_doc_1": null,
"guid_comp": "3f2a7c10-9b4e-4d61-8a22-1c5e7f9d0b33",
"doc_entity_hdr_guid": "e7b2d48f-1c93-4a05-8e67-4f9a3b1d5c26",
"doc_reference": "INV-ACW-2611",
"doc_ccy": "MYR",
"amount_txn": 600.0,
"amount_open_balance": 600.0,
"posting_status": "DRAFT",
"status": "ACTIVE",
"revision": "1e5c9a37-8b40-4d26-9f73-2a8e6c4b1d95",
"created_date": "2026-10-21T04:33:35.640043256Z"
},
"bl_fi_generic_doc_line": [
{ "item_code": "GS-AC-CABLE-C-1M", "quantity_base": 50.0, "amount_txn": 600.0, "status": "ACTIVE" }
],
"bl_fi_generic_doc_event": [],
"bl_fi_generic_doc_ext": [],
"bl_fi_generic_doc_link": [],
"bl_fi_generic_doc_budget_sub_line": []
},
"message": ""
}Keep the returned guid and revision: the update needs both, and payment vouchers settle against the
guid. server_doc_1 is null in this response only: the tenant number is written just after it, so read the invoice back to see 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-ep
Send guid and the revision you last read. Only update documents still in DRAFT. Void and re-issue a finalized one.
curl -X PUT "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": {
"guid": "4d8b2f61-3a07-4e95-b12c-7f6a9d3e5c84",
"revision": "1e5c9a37-8b40-4d26-9f73-2a8e6c4b1d95",
"server_doc_type": "INTERNAL_PURCHASE_INVOICE",
"doc_source_type": "INTERNAL",
"guid_comp": "3f2a7c10-9b4e-4d61-8a22-1c5e7f9d0b33",
"guid_branch": "8c1e4a27-5d39-4b62-9f08-2a7d6c3e1b54",
"guid_store": "6b8d1f42-7e35-4c09-ad81-1d4e8b2c5a70",
"doc_entity_hdr_guid": "e7b2d48f-1c93-4a05-8e67-4f9a3b1d5c26",
"doc_ccy": "MYR",
"doc_reference": "INV-ACW-2611",
"doc_desc": "USB-C cables, October (supplier invoice INV-ACW-2611)",
"amount_signum": -1,
"amount_txn": 600.00,
"posting_status": "DRAFT",
"status": "ACTIVE"
}
}'{
"code": "OK_RESPONSE",
"data": {
"bl_fi_generic_doc_hdr": {
"guid": "4d8b2f61-3a07-4e95-b12c-7f6a9d3e5c84",
"doc_desc": "USB-C cables, October (supplier invoice INV-ACW-2611)",
"posting_status": "DRAFT",
"revision": "6a2f8d14-0c57-4b93-a1e8-3d9b7c5f2e60"
},
"bl_fi_generic_doc_line": [],
"bl_fi_generic_doc_event": [],
"bl_fi_generic_doc_ext": [],
"bl_fi_generic_doc_link": [],
"bl_fi_generic_doc_budget_sub_line": []
},
"message": ""
}A header-only update keeps the lines. This call sent no lines, so the answer echoes none, but the
invoice still has its cable line: read it back to see it. The revision changes on every update.
Send an older one and the update is refused with 400, EXCEPTION_INCONSISTENT_DATA and
RACE_CONDITION — someone saved the invoice after you read it, so read it again before retrying.
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"}'{
"code": "OK_RESPONSE",
"data": {
"bl_fi_generic_doc_hdr": {
"guid": "4d8b2f61-3a07-4e95-b12c-7f6a9d3e5c84",
"server_doc_1": "1000187",
"posting_status": "FINAL",
"amount_txn": 600.0
},
"bl_fi_generic_doc_line": [
{ "item_code": "GS-AC-CABLE-C-1M", "quantity_base": 50.0, "amount_txn": 600.0 }
]
},
"message": ""
}The answer comes before the posting is finished. The journal and stock postings run in the
background and write back to the invoice, changing its revision (twice within about two seconds in
our test; nothing bounds it). An update or void sent straight away is refused with 400,
EXCEPTION_INCONSISTENT_DATA and RACE_CONDITION. Read the invoice again and send its current
revision; on a refusal, wait and retry.
Void a purchase invoice
PUT /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/void/{guid}
The body must carry posting_status; void_reason is optional and kept on the invoice.
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 '{"posting_status": "VOID", "void_reason": "Supplier re-issued the invoice"}'{
"code": "OK_RESPONSE",
"data": {
"bl_fi_generic_doc_hdr": {
"guid": "4d8b2f61-3a07-4e95-b12c-7f6a9d3e5c84",
"server_doc_1": "1000187",
"posting_status": "VOID",
"void_reason": "Supplier re-issued the invoice"
}
},
"message": ""
}200. The route sets VOID only when the body has a
posting_status key; with {} it saves the invoice unchanged and returns OK_RESPONSE, and the
invoice stays FINAL. Check posting_status in the answer, not the status code.Delete, discard and purge
The delete route does not stop you removing a posted invoice. It removed a voided invoice when we
tried it, and nothing in the route checks for FINAL either. Deleting is your decision to make before the call: keep
it for drafts, and void anything that was ever finalised.
| Path | What the route checks |
|---|---|
DELETE /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/{guid} | The delete permission only. It does not look at posting_status. |
PUT /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/discard/etl-ep/{guid} | Refuses a FINAL or VOID invoice. |
DELETE /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/purge/etl-ep/{guid} | Refuses a FINAL invoice, and any invoice whose status is not DELETED and whose posting status is not DISCARDED. |
A delete answers with data: true; afterwards get-by-guid answers 404 and lists leave it out:
{ "code": "OK_RESPONSE", "data": true, "message": "" }Get one purchase invoice
GET /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/{guid}
curl "https://api-etl.akaun.com/core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/4d8b2f61-3a07-4e95-b12c-7f6a9d3e5c84" \
-H "AccessId: YOUR_ACCESS_ID" \
-H "AccessKey: YOUR_ACCESS_KEY" \
-H "tenantCode: YOUR_TENANT_CODE"The whole container, lines included, here the voided invoice from the run above:
{
"code": "OK_RESPONSE",
"data": {
"bl_fi_generic_doc_hdr": {
"guid": "4d8b2f61-3a07-4e95-b12c-7f6a9d3e5c84",
"server_doc_1": "1000187",
"doc_desc": "USB-C cables, October (supplier invoice INV-ACW-2611)",
"posting_status": "VOID",
"void_reason": "Supplier re-issued the invoice"
},
"bl_fi_generic_doc_line": [
{ "item_code": "GS-AC-CABLE-C-1M", "quantity_base": 50.0, "amount_txn": 600.0, "status": "ACTIVE" }
],
"bl_fi_generic_doc_event": [],
"bl_fi_generic_doc_ext": [],
"bl_fi_generic_doc_link": [],
"bl_fi_generic_doc_budget_sub_line": []
},
"message": ""
}An unknown guid answers 404 with CLIENT_VALIDATION_GUID_DOES_NOT_EXIST.
Query purchase invoices
GET /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/query
The 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, client_doc_1_like, client_doc_1s, client_doc_2 | string / string set | Your own system’s number: exact, partial, or several (repeat the parameter). |
client_doc_type, client_doc_types | string / string set | Your own system’s document type. Repeat for several. |
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"The answer is the paged envelope, one container per invoice. Shortened to one invoice and the header fields this query filtered on:
{
"totalRecords": 3,
"offset": 0,
"limit": 50,
"code": "OK_RESPONSE",
"message": "",
"data": [
{
"bl_fi_generic_doc_hdr": {
"guid": "4d8b2f61-3a07-4e95-b12c-7f6a9d3e5c84",
"server_doc_type": "INTERNAL_PURCHASE_INVOICE",
"server_doc_1": "1000412",
"doc_entity_hdr_guid": "e7b2d48f-1c93-4a05-8e67-4f9a3b1d5c26",
"posting_status": "FINAL",
"doc_ccy": "MYR",
"amount_txn": 12850.00,
"status": "ACTIVE"
},
"bl_fi_generic_doc_line": [
{ "item_code": "ACC-PB-20K", "quantity_base": 50, "amount_txn": 12850.00, "status": "ACTIVE" }
],
"bl_fi_generic_doc_event": [],
"bl_fi_generic_doc_ext": [],
"bl_fi_generic_doc_link": [],
"bl_fi_generic_doc_budget_sub_line": []
}
]
}Without a read grant the same query is refused outright, with 403,
CLIENT_AUTH_USER_NOT_AUTHORIZED and no rows.
A read grant with no target does not open this query. The query keeps only grants of
TNT_API_DOC_INTERNAL_PURCHASE_INVOICE_READ_TGT_GUID that name a target table, and returns the rows
of the companies, branches and stores they name. A grant that names a target table but leaves the
record empty counts as all of them, so for every company’s invoices grant it against companies with
no company chosen. A grant with no target at all is refused with the answer above, unless the user is
the tenant owner or administrator.
Print a purchase invoice
GET /core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/print-jasper-pdf/{guid}
The query parameter printableFormatHdrGuid is required: it names the printable format, set up in the
tenant, that lays the invoice out.
curl -G "https://api-etl.akaun.com/core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/print-jasper-pdf/4d8b2f61-3a07-4e95-b12c-7f6a9d3e5c84" \
-H "AccessId: YOUR_ACCESS_ID" \
-H "AccessKey: YOUR_ACCESS_KEY" \
-H "tenantCode: YOUR_TENANT_CODE" \
--data-urlencode "printableFormatHdrGuid=2b6e9d41-7c03-4f58-a1d2-8e4c6b9f3a17" \
-o PI-1000187.pdfThe answer is the PDF itself, not the JSON envelope:
HTTP/1.1 200 OK
Content-Type: application/pdfEach successful print adds one to the invoice’s print_count and stamps print_date, whoever
printed it. Leave the parameter out and nothing is printed:
curl "https://api-etl.akaun.com/core2/tnt/dm/erp/gen-doc/internal-purchase-invoices/etl-ep/print-jasper-pdf/4d8b2f61-3a07-4e95-b12c-7f6a9d3e5c84" \
-H "AccessId: YOUR_ACCESS_ID" \
-H "AccessKey: YOUR_ACCESS_KEY" \
-H "tenantCode: YOUR_TENANT_CODE"{
"code": "CLIENT_VALIDATION_GENERAL",
"data": null,
"message": "Required request parameter 'printableFormatHdrGuid' for method parameter type UUID is not present"
}GET .../etl-ep/batch-print-jasper-pdf/{guids} takes several GUIDs and returns a ZIP.
Related
- Purchasing API — the full generated route list for
GenericDocumentControllerand the 40 purchase document slugs - Payment Voucher — how an invoice on this page is settled
- Supplier — the entity the invoice is raised against
- Data API — hosts, headers, paging, the response envelope and limits