Skip to content

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:

  1. POST /internal-purchase-invoices/etl-ep as a DRAFT, with server_doc_type, amount_signum -1 and doc_source_type INTERNAL on the header (create).
  2. Check it, then PUT …/etl-ep/update-posting-status/{guid} with {"posting_status": "FINAL"}. Re-read before you void it; on RACE_CONDITION, wait and retry (finalize).
  3. 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-invoicesINTERNAL_PURCHASE_INVOICE
internal-payment-vouchersINTERNAL_PAYMENT_VOUCHER
internal-reimbursement-payment-vouchersINTERNAL_REIMBURSEMENT_PAYMENT_VOUCHER
internal-purchase-ordersINTERNAL_PURCHASE_ORDER
internal-purchase-returnsINTERNAL_PURCHASE_RETURN
internal-purchase-credit-notesINTERNAL_PURCHASE_CREDIT_NOTE
internal-purchase-debit-notesINTERNAL_PURCHASE_DEBIT_NOTE
internal-purchase-goods-received-notesINTERNAL_PURCHASE_GOODS_RECEIVED_NOTE
internal-receipt-vouchersINTERNAL_RECEIPT_VOUCHER
internal-sales-invoicesINTERNAL_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

OperationPermission
CreateTNT_API_DOC_INTERNAL_PURCHASE_INVOICE_CREATE_TGT_GUID
ReadTNT_API_DOC_INTERNAL_PURCHASE_INVOICE_READ_TGT_GUID
UpdateTNT_API_DOC_INTERNAL_PURCHASE_INVOICE_UPDATE_TGT_GUID
DeleteTNT_API_DOC_INTERNAL_PURCHASE_INVOICE_DELETE_TGT_GUID
Document permissions are target-scoped. The _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

OperationPath (relative to the base path)
CreatePOST /{docType}/etl-ep
Create severalPOST /{docType}/etl-ep/multi
UpdatePUT /{docType}/etl-ep
DeleteDELETE /{docType}/etl-ep/{guid}
List allGET /{docType}/etl-ep
Get oneGET /{docType}/etl-ep/{guid}
QueryGET /{docType}/etl-ep/query
Query with a POST bodyPOST /{docType}/etl-ep/query
Query across all doc typesGET /etl-ep/query
List valid document typesGET /etl-ep/server-doc-types

Lifecycle

OperationPath
Set posting statusPUT /{docType}/etl-ep/update-posting-status/{guid}
Clear posting statusPUT /{docType}/set-posting-status-null/etl-ep/{guid}
VoidPUT /{docType}/etl-ep/void/{guid}
DiscardPUT /{docType}/discard/etl-ep/{guid}
PurgeDELETE /{docType}/purge/etl-ep/{guid}
Multi-document operationPOST /etl-ep/multi-doc

Reporting and files

OperationPath
Query snapshotGET /{docType}/etl-ep/query/snapshot
Query snapshot with a POST bodyPOST /{docType}/etl-ep/query/snapshot
Query by transaction typeGET /{docType}/etl-ep/type/query
Print to PDFGET /{docType}/etl-ep/print-jasper-pdf/{guid}
Batch printGET /{docType}/etl-ep/batch-print-jasper-pdf/{guids}
Batch print with a POST bodyPOST /{docType}/etl-ep/batch-print-jasper-pdf
Attach a filePUT /{docType}/etl-ep/add-files/{hdrGuid}
Replace a filePUT /{docType}/etl-ep/replace-file/{extGuid}
Get a fileGET /{docType}/etl-ep/file/{extGuid}
Delete a fileDELETE /{docType}/etl-ep/file/{extGuid}
UnbundleGET /{docType}/bundle/unwrapped/etl-ep/{guid}

Standard ETL endpoints

Higher-throughput variants intended for bulk import:

OperationPath
Create (standard)POST /standard/{docType}/etl-ep
Create, returning only the GUIDPOST /custom-prev/standard/{docType}/etl-ep
Create via JSON import (async)POST /custom/standard/{docType}/etl-ep
Check JSON import statusGET /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.

FieldTypeOn createDescription
guidUUIDOptionalGenerated if omitted. Required on update.
guid_compUUIDRequiredCompany the invoice belongs to. Also a permission target.
guid_storeUUIDRequiredStore/location. Also a permission target.
guid_branchUUIDOptionalBranch. Also a permission target.
doc_entity_hdr_guidUUIDRequiredThe supplier being invoiced.
server_doc_typestringRequiredINTERNAL_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_signumdecimalRequired-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_typestringRecommendedYour 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_typestringRequiredWhere the document came from. Use INTERNAL for documents you create. Leaving it out fails with GENERIC_DOC_HDR_INVALID_SOURCETYPE.
doc_ccystringRecommendedDocument currency, e.g. MYR.
date_txndatetimeRecommendedTransaction date. Defaults to now.
due_datedatetimeOptionalPayment due date.
doc_referencestringOptionalThe supplier’s invoice number.
doc_descstringOptionalShort description.
doc_remarksstringOptionalFree-text remarks.
doc_reference_tax_numstringOptionalTax invoice number.
doc_reference_tax_datedatetimeOptionalTax invoice date.
amount_stddecimalRecommendedGross amount before discount.
amount_discountdecimalOptionalTotal discount.
amount_netdecimalRecommendedNet amount after discount, before tax.
amount_tax_gstdecimalOptionalGST/SST amount.
amount_tax_whtdecimalOptionalWithholding tax amount.
amount_txndecimalRecommendedTotal payable.
amount_open_balancedecimalOptionalOutstanding balance. Maintained by the system as payments settle.
foreign_ccybooleanOptionalSet when the document is in a foreign currency.
base_doc_ccystringOptionalBase currency for a foreign-currency document.
base_doc_xratedecimalOptionalExchange rate to the base currency.
posting_statusstringOptionalDRAFT or FINAL. See the lifecycle section.
statusstringOptionalDefaults to ACTIVE.
revisionUUIDOptionalGenerated if omitted. Send the value you read back when updating.
client_key, client_sourcestringOptionalYour system’s key for this document, and your system’s name.
client_doc_1 … client_doc_5stringOptionalWhatever your own system calls this document. Kept beside BigLedger’s numbers, never instead of them — the generic document.
client_valuestringOptionalFree-form value for your own use.
property_jsonobjectOptionalArbitrary extra properties.
credit_terms_jsonobjectOptionalCredit terms.
billing_jsonobjectOptionalBilling details.
server_doc_1 … server_doc_5stringServer-assignedRunning 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_locationstringServer-maintainedCode 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_contradecimalServer-maintainedAR/AP settlement tracking, updated as payment vouchers settle the invoice.
amount_internal_settlementdecimalServer-maintainedAmount already applied against this invoice.
doc_entity_hdr_jsonobjectOptional — filled from the supplier record when you omit itThe 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_jsonobjectOptionalThe delivery entity’s details, copied onto the document in the same way. Nothing fills this one for you.
posting_inventory, posting_journal, posting_bundlestringServer-maintainedWhether each posting step has run.
pin_code, pin_type, pin_hash, pin_algorithmstringServer-assignedGenerated on create when no PIN is supplied. Do not set these.
created_date, updated_datedatetimeServer-assigned
created_by_subject_guid, updated_by_subject_guidUUIDServer-assignedSet 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_guid at the moment your call arrives: entityId (the supplier code), entityType, entityName, idNumber, identityType, email, phoneNumber, description, status and currency — and currency is hard-coded to MYR. 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 PUT that omits it on a document that already has one leaves it alone too, because the fill only runs on an empty block. The fill covers doc_entity_hdr_json only: delivery_entity_json and billing_json are 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

FieldTypeOn createDescription
guidUUIDOptionalGenerated if omitted.
generic_doc_hdr_guidUUIDSet for youFilled in from the header.
item_guidUUIDRequiredThe item being purchased.
item_codestringRequiredThe item’s code.
quantity_basedecimalRequiredQuantity in the item’s base unit.
amount_stddecimalRequiredGross line amount before discount.
server_doc_typestringServer-filledInherited from the header.
amount_signumdecimalServer-filledForced to -1. Anything you send is discarded.
quantity_signumdecimalServer-filledForced to 1. Anything you send is discarded.
item_namestringRecommendedItem name as it should appear on the document.
item_descstringOptionalItem description.
item_remarksstringOptionalLine remarks.
uomstringOptionalUnit of measure.
qty_by_uomdecimalOptionalQuantity in that unit.
uom_to_base_ratiodecimalOptionalConversion factor to the base unit.
unit_price_by_uomdecimalOptionalUnit price in that unit.
amount_discountdecimalOptionalLine discount.
amount_netdecimalRecommendedLine net after discount, before tax.
amount_txndecimalRecommendedLine total.
tax_gst_code, tax_gst_type, tax_gst_rate, amount_tax_gststring / decimalOptionalGST/SST on the line.
tax_wht_code, tax_wht_type, tax_wht_rate, amount_tax_whtstring / decimalOptionalWithholding tax on the line.
guid_glcodeUUIDOptionalGL code to post this line to.
guid_comp, guid_branch, guid_storeUUIDOptionalDefault to the header’s values.
doc_ccystringOptionalLine currency. Defaults to the header’s.
statusstringOptionalDefaults to ACTIVE.
revisionUUIDOptionalGenerated 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:

ValueMeaning
DRAFTNot final. Freely editable.
FINALFinal 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.

ActionWhat it does
DiscardMarks the document discarded. Reversible bookkeeping-wise; the record stays.
VoidReverses a finalized document. This is the correct way to cancel a posted invoice.
DeleteRemoves the document. Only appropriate for a draft that has not been posted.
PurgePermanently 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:

FieldTypeDescription
posting_statusstringDRAFT or FINAL.
validate_serial_signum_zeroboolean stringOptional. "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": ""
}
An empty body voids nothing and still answers 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.

PathWhat 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:

ParameterTypeDescription
has_linebooleanInclude line rows in the response. Set this to true or you get headers only.
has_extbooleanInclude extension rows.
has_linkbooleanInclude document links.
has_eventbooleanInclude events.
has_attachmentbooleanInclude attachments.
guid_compUUIDFilter by company.
guid_branchUUIDFilter by branch.
guid_storestring (UUID)Filter by store.
doc_entity_hdr_guidUUIDFilter by supplier.
server_doc_1stringExact BigLedger document number.
client_doc_1, client_doc_1_like, client_doc_1s, client_doc_2string / string setYour own system’s number: exact, partial, or several (repeat the parameter).
client_doc_type, client_doc_typesstring / string setYour own system’s document type. Repeat for several.
doc_source_typestringDocument source.
doc_descstringMatch on description.
hdr_doc_ccystringFilter by currency.
hdr_posting_statusstringDRAFT or FINAL.
arap_bal_rangestringFilter by outstanding balance range.
hdr_client_key, hdr_client_sourcestringFilter by your system’s identifiers.
code_company, code_branch, code_locationstringFilter by code instead of GUID.
contact_hdr_guid, contact_key_guid, member_guidUUIDContact and member filters.
file_batch_idstringFilter 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.pdf

The answer is the PDF itself, not the JSON envelope:

HTTP/1.1 200 OK
Content-Type: application/pdf

Each 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 GenericDocumentController and 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
Last updated on