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 situation | Open | Why that page |
|---|---|---|
| You finalised a document and want to know whether it became an e-invoice | Document Query APIs | Query 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 consolidated | E-Invoice Submission | The 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 details | E-Invoice Submission | Save and Resubmit sends the e-invoice record again, so the correction goes on the record |
| You want to stop a refusal before it happens | LHDN Integration APIs | Validate the buyer’s tax number with LHDN first |
| An e-invoice went out wrong and was validated | Cancellation & Rejection | Two routes, a request with approval or a direct cancellation, both inside 72 hours |
| The buyer says the PDF never arrived | Get Notifications | The queue and history dashboards show waiting, sent and failed |
| You need an e-invoice route these pages do not name | E-Invoice API | Generated from the backend’s route table, controller by controller |
| You want to be told when the status changes, instead of asking | Webhooks | E-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.
- 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
FINALis 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. - 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.
- The submission type you sent routes it.
CONSOLIDATEDsends it to the Batch Pool to wait for the month’s consolidated e-invoice. ACONSOLIDATEDsales 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). - 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.
- The status lives on the e-invoice record, in
einvoice_document_status. It readsIN_QUEUEwhile it waits to be sent, thenSubmittedonce LHDN has accepted the submission, thenValidorInvalid, and later perhapsCancelled. The record’ssubmission_statusstops atSubmittedand never readsValid, so filter and test oneinvoice_document_status. A document of more than 200 lines is split into numbered parts, each with its own record. - 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,
InvalidorIN_QUEUEwith 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.
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.When it goes wrong
| What you see | Usual cause | What to do |
|---|---|---|
Your document is FINAL, and after half an hour no e-invoice record and no pool row exists | It 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 waiting | Create 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 day | The send failed: LHDN refused the submission, or it never got there (an expired token, a time-out). Nothing retries it | Read the error on the record and on its submission queue row, fix the cause, then submit-to-lhdn |
Invalid | LHDN refused the e-invoice; its reasons are in validation_response | Read 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 more | The poller gave up: a check is dropped after ten attempts or three days | Call refresh-lhdn-document-status for the record |
| A record you were tracking has been deleted after month-end | The consolidation run retired a refused record under RM 10,000 into the consolidated e-invoice | Nothing 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 none | It filtered hdr_submission_statuses, which stops at Submitted | Filter hdr_einvoice_document_statuses |
| A cancellation is refused with “Passed 72 hours from validation date time” | LHDN’s window has closed | Raise 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 row | The company’s intermediary authorisation lapsed on MyInvois, so LHDN refused the token refresh and the last token has run out | Re-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.
- 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. - LHDN accepted it.
einvoice_document_statusreadsValid, withlhdn_document_guidandvalidation_datetimefilled. That is the answer your finance team needs. - Nothing is left behind. Once a day, count the records at
Invalid, and atIN_QUEUEolder 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
Valide-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-eproutes 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’sValid. - 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.