Skip to content
E-Invoice API Reference

E-Invoice API Reference

“I finalised the invoice and nothing was submitted to LHDN.” When your own system creates the invoice, that sentence has three usual answers: the e-invoice is still waiting for a scheduled run, it was never created, or LHDN refused it. These pages are how your code tells which, and what to do about each. They cover the HTTP side of BigLedger’s MyInvois e-invoicing: following an e-invoice after Finalise, fixing and resubmitting it, cancelling it, checking a buyer’s tax number, and the e-mail that carries the validated PDF to the buyer.

Which page answers your question

Your situationOpenWhy that page
You finalised a document and want to know whether it became an e-invoiceDocument Query APIsQuery by your document’s guid; the record carries the status and LHDN’s answer
You are about to send sales documents and must decide individual or consolidatedE-Invoice SubmissionThe submission type you send decides the pool, and the RM 10,000 rule overrides it
LHDN refused an e-invoice and you know the right buyer detailsE-Invoice SubmissionSave and Resubmit sends the e-invoice record again, so the correction goes on the record
You want to stop a refusal before it happensLHDN Integration APIsValidate the buyer’s tax number with LHDN first
An e-invoice went out wrong and was validatedCancellation & RejectionTwo routes, a request with approval or a direct cancellation, both inside 72 hours
The buyer says the PDF never arrivedGet NotificationsThe queue and history dashboards show waiting, sent and failed
You need an e-invoice route these pages do not nameE-Invoice APIGenerated from the backend’s route table, controller by controller
You want to be told when the status changes, instead of askingWebhooksE-invoice events use a separate feed, switched on per company

How big the e-invoice API is, and what these pages cover

As at backend commit 4623d9e7ae, whose route table was regenerated on 7 October 2026, 857 routes sit under the two e-invoice paths, /core2/tnt/dm/erp/e-invoice/ (769) and /core2/tnt/dm/erp/fi/e-invoice/ (88). They are served by 98 controllers. 78 of those routes carry the etl-ep segment built for a server-to-server key. Most of the rest, 714, are backoffice-ep routes, the ones the My E-Invoice Admin screens call. The figures are counted from the controllers’ mapping annotations and move whenever the backend adds a controller, so read them as a size. Routes for Peppol and the e-invoice retry queue sit under other paths and are not in the count.

The five pages here document 16 of those routes, the ones the workflows above need, and every path on them is a row in that table. Most are backoffice-ep routes. Your access key still works on them: their handler reads a signed-in token when one is sent and your key when none is, so send no Authorization header. Some have an etl-ep twin as well, such as the record reads, Save and Resubmit and the tax-number checks; the rejection and direct-cancellation routes, the LHDN document search and the e-mail dashboards do not (a separate bulk cancellation, POST …/fi/e-invoice/to-irbs/bulk-cancellation/etl-ep, is key-first). Which credential works where has the general rule. The pages show api-etl.akaun.com, the host for data traffic; api.akaun.com answers the same paths (the two hosts). The generated E-Invoice API page lists the family’s etl-ep routes controller by controller; the incoming side, documents pulled down from MyInvois, is on no page yet and is only in the route table.

What happens after you finalise

None of this shows in a response. It decides whether, and when, LHDN sees your document.

  1. The e-invoice starts at Finalise, not at create, with one exception. A document you create is stored and numbered and starts no follow-on job. Finalise is the separate call that starts them, and the e-invoice entry is one of them. Since mid-October 2026 a document you create as FINAL is handed to the e-invoice entry and to nothing else, so it can become an e-invoice with no journal behind it. Draft, then Finalise has the two calls.
  2. One background job decides once whether it is an e-invoice. A moment after Finalise answers, it checks three things: the company’s e-invoice status is ENABLED, the document type is one that is reported, and the same document is not already waiting. If any fails, nothing is written and no error is raised. It does not look again, so a company switched on later leaves the earlier documents outside.
  3. The submission type you sent routes it. CONSOLIDATED sends it to the Batch Pool to wait for the month’s consolidated e-invoice. A CONSOLIDATED sales invoice or cash bill of RM 10,000 or more goes to the Individual Pool instead. Anything else goes to the posting queue, where the next run builds the e-invoice record. That run takes the buyer from the e-invoice buyer block you sent on the document, or from the customer record when you sent none, and checks LHDN’s mandatory fields. It queues the record for submission if they pass, and parks it in a pool if they fail; one sent with no type fails into the Batch Pool (the two routes there).
  4. Building, sending and checking are separate scheduled runs. On a tenant’s default schedule each runs every ten minutes: one builds the record, one sends it to LHDN, one asks LHDN for the verdict. Expect a few runs between your Finalise and a final answer. Your tenant’s actual clocks are on the Scheduler applet.
  5. The status lives on the e-invoice record, in einvoice_document_status. It reads IN_QUEUE while it waits to be sent, then Submitted once LHDN has accepted the submission, then Valid or Invalid, and later perhaps Cancelled. The record’s submission_status stops at Submitted and never reads Valid, so filter and test on einvoice_document_status. A document of more than 200 lines is split into numbered parts, each with its own record.
  6. Month-end runs on a clock of its own, and it can take records back. Since 13 October 2026 the consolidation run also collects the month’s records that LHDN refused, Invalid or IN_QUEUE with LHDN’s refusal on them, if they are under RM 10,000 and of a sales type. It retires each record and reports the sale inside the consolidated e-invoice under the General Public buyer. It stamps the document Consolidated whatever submission type you sent and whatever the company’s Disable Consolidated Submission switch says. Whether that is meant for every buyer is a question we have put to BigLedger. Until it is answered, a refused e-invoice whose buyer needs one of their own is a job with a deadline: fix and resubmit it before the consolidation run fires. A record that never reached LHDN is left for the submission run.

Two things you can do that the schedule does not. POST …/fi/e-invoice/to-irbs/submit-to-lhdn/etl-ep with the records’ toIrbHdrGuids sends them to LHDN in the request, instead of waiting for the next run. POST …/to-irbs/refresh-lhdn-document-status/etl-ep asks LHDN for the current verdict now. The first needs a separate permission, API_TNT_DM_ERP_MY_E_INVOICE_GENDOC_TO_IRB_SUBMISSION_QUEUE_UPDATE.

A correction goes on the record, not on your document. Once the record exists, Save and Resubmit sends the record as it stands and copies its buyer and supplier details back onto your document. A fix you make only on the document is therefore overwritten by the old values. Change the record first, then resubmit.

What these APIs will not do

Stated plainly, so you can plan around it.

Finalise does not answer with LHDN’s verdict. Its 200 means the document is final and its follow-on jobs are queued. Whether an e-invoice was even created is something you read back later.
The ordinary webhook topics do not cover e-invoices. E-invoice events travel on a separate lifecycle feed, switched on per company, and BigLedger turns it on. Its delivery job is not on a tenant’s default schedule. Until you have it, poll the record.
A dropped document makes no noise. When the job in step 2 declines a document, it writes nothing anywhere, so the only sign is a record that never appears. Your own check has to look for the absence.
Cancellation closes 72 hours after validation. BigLedger counts whole hours from the validation time it stored and refuses after that. The correction is then a credit note, not a cancellation.

When it goes wrong

What you seeUsual causeWhat to do
Your document is FINAL, and after half an hour no e-invoice record and no pool row existsIt was created as FINAL before mid-October 2026 and never finalised; or the job in step 2 declined it: company not ENABLED at that moment, a type that is not reported, or already waitingCreate as a draft and finalise. For a declined document, send BigLedger the document numbers, the company and when they were finalised (what to send)
einvoice_document_status stays IN_QUEUE for more than a dayThe send failed: LHDN refused the submission, or it never got there (an expired token, a time-out). Nothing retries itRead the error on the record and on its submission queue row, fix the cause, then submit-to-lhdn
InvalidLHDN refused the e-invoice; its reasons are in validation_responseRead the record, correct its buyer fields and PUT it back to …/to-irbs/etl-ep, then Save and Resubmit. Correct the customer too, or the next sale fails the same way
Submitted for three days or moreThe poller gave up: a check is dropped after ten attempts or three daysCall refresh-lhdn-document-status for the record
A record you were tracking has been deleted after month-endThe consolidation run retired a refused record under RM 10,000 into the consolidated e-invoiceNothing is lost; the sale is in the consolidated e-invoice. Resubmit before the run next time if the buyer needs their own
A query for Valid records returns noneIt filtered hdr_submission_statuses, which stops at SubmittedFilter hdr_einvoice_document_statuses
A cancellation is refused with “Passed 72 hours from validation date time”LHDN’s window has closedRaise a credit note against the e-invoice
Every submission for one company fails at once, and the record stays IN_QUEUE with LHDN’s refusal on its submission queue rowThe company’s intermediary authorisation lapsed on MyInvois, so LHDN refused the token refresh and the last token has run outRe-authorise BigLedger on MyInvois; the next background token refresh then succeeds. Send the waiting records again with submit-to-lhdn

How you know it worked

Three reads settle it, and none needs anyone watching a screen.

  1. The record exists. Twenty minutes or so after Finalise, query GET …/fi/e-invoice/to-irbs/etl-ep/query?hdr_generic_doc_hdr_guids=<your document guid>. One record comes back, or one per part for a split document. No record means the document is in a pool, waiting for a person or for month-end, or that it was declined. The admin applet shows the pools.
  2. LHDN accepted it. einvoice_document_status reads Valid, with lhdn_document_guid and validation_datetime filled. That is the answer your finance team needs.
  3. Nothing is left behind. Once a day, count the records at Invalid, and at IN_QUEUE older than a day. Somebody in finance owns that list, works it before the month’s consolidation run, and decides which buyers need an e-invoice of their own.

Where this sits in the product

  • What must exist first. The company is authorised as an intermediary on MyInvois, its identity is complete and its e-invoice status is ENABLED before you finalise anything: MyInvois setup, Steps 1 to 4. Your documents carry the submission type and, for a business buyer, the buyer block. Without the buyer block, the customer record has to be complete.
  • What becomes possible. A Valid e-invoice is e-mailed to the buyer as a PDF (Get Notifications). The receipts you sent as consolidated become one monthly consolidated e-invoice. The month’s Discrepancies Report in the admin applet compares your documents with what LHDN holds.
  • The siblings, and when to use which. These pages are written workflows over 16 routes; the generated E-Invoice API page lists the family’s etl-ep routes and says nothing about behaviour. The My E-Invoice Admin applet does the same work through screens, for a person. Peppol is a different network: it fills its own waiting queue at Finalise and does not wait for LHDN’s Valid.
  • What breaks if it is wrong. A blank or wrong submission type, or a missing buyer block, can send a business customer’s purchase into a General Public e-invoice that does not name them. A company not yet ENABLED at Finalise leaves documents unreported, and they appear on the Discrepancies Report as missing. A cancellation that voids the source document reverses its journal and stock postings, as any void does.
  • What it is used alongside. The lifecycle feed together with a daily query: the query makes your integration correct, the feed only makes it quick. On the finance side, The Month-End E-Invoice Cycle is the run order the refused list has to be cleared within.

Related documentation

Last updated on