E-Invoice Submission Mechanics
Watch these as presentations — 3 on this page, slides with narration.
You are the person who gets asked “did that invoice reach LHDN, and if not, where is it?” — usually by a customer who wants the validated e-invoice, sometimes by an auditor who wants last month’s submissions in a file. By the end of this page you will know what BigLedger actually stores between a finalised document and LHDN, be able to find any one document in those records in under a minute, export a month for whoever is asking, and read the monthly discrepancy report without guessing at what “missing” means. Reading it takes about fifteen minutes; there is nothing to configure.
The routing rules — which pool a document lands in and why — are on Pools and queues and Consolidated e-invoice. This page is about the records those rules produce.
Meet GadgetSphere
GadgetSphere Sdn Bhd (GS) runs 22 retail branches. In March 2026 they finalised about 38,000 cash bills, almost all of them below RM 10,000 and to walk-in shoppers, so they were reported inside consolidated e-invoices, built by branch: one line per consecutive run of receipt numbers, and no more than 200 lines on any one e-invoice. The distribution arm (GSD) issued 310 invoices to corporate buyers, each submitted individually. Four of those 310 are sitting in the Individual Pool because the buyer’s tax identification number (TIN) was never captured. That is the month we will trace.
The record that goes to LHDN is not your invoice
When a document passes the mandatory-field check, BigLedger does not send the document. It builds a separate record — the to-IRB header — and sends that. It is a frozen copy: the document numbers, the branch, the buyer and supplier as they were at that moment (name, TIN, identity type and number, address, contact), the totals in the document currency and the rate, the billing period for a consolidated e-invoice. Then, as LHDN answers, the same record collects what came back: the submission and document identifiers LHDN assigned, the unique reference number, the validation link and QR code, the digital signature, and the document status (Submitted, Valid, Invalid, Cancelled).
The record is the to-IRB e-invoice. You see it as Internal Submission → To IRB E-Invoice in the My E-Invoice Admin applet, and every row of the Master List is one of these headers.
Two consequences follow from “frozen copy”, and both catch people out:
- Correcting the customer record after submission changes nothing here. Fix the TIN on the customer and the e-invoice still carries the old one. The route is to correct the record itself and use Save and Resubmit on the to-IRB row (or on the pool row, if it never got this far). On the to-IRB row that copies your corrections back to the document and puts the same record back in the submission queue; it does not make a new one. Resubmit as New E-invoice is the action that creates a new record — see Cancel and correct.
- The document is told where its record is, once. At creation, BigLedger writes the header’s identifier and LHDN reference back onto the sales document (the To IRB fields on the document header). If those fields are blank on a finalised document, no to-IRB record exists for it: it is in a pool, or it never entered e-invoicing at all.
Every charge line of the document becomes a to-IRB line — the builder keeps only lines whose transaction type is PNS, so a payment or settlement line on the same document never appears — carrying the five things LHDN insists on per line: the classification code, the item name, the unit price, the taxable-type code and the tax amount. A document with any of the five null on any line does not get a header at all; it goes to a pool with the reason attached (the test is for a missing value only, so an empty string passes).
Four of those five can never be null by the time the check runs, and this is the single most misread thing about line-level e-invoice failures. The same builder that assembles the line also applies defaults to it: a blank classification becomes 022 Others, a blank e-invoice unit becomes H87 piece, a null unit price becomes zero, a null tax amount becomes zero, and the taxable type is derived from that tax amount — 06 Not Applicable when it is zero, 01 Sales Tax when it is not. Only a null item name can still pool a document at line level. So an item with an empty E-Invoice tab does not pool, does not reject and does not warn: it is submitted, accepted, and classified 022 Others. Nothing on any screen afterwards distinguishes a classification somebody chose from one the builder supplied, so the only place to catch it is the item.
The link back to your documents
A to-IRB header points at the document it was built from. That is enough for an individual submission — one header, one document — but a consolidated e-invoice is one header built from hundreds of receipts, and the header can only point at one of them. So BigLedger keeps a second table, the match link: one row per (to-IRB header, document) pair.
- For GSD’s 306 individually submitted invoices, there are 306 link rows, each duplicating what the header already says.
- For branch
GS-KV-01, whose 4,000 March receipts were consolidated by branch, there are 4,000 link rows — one per receipt — spread over as many consolidated headers as that branch’s numbering needed. A by-branch consolidated e-invoice carries at most 200 lines, and one line covers a whole consecutive range of receipt numbers, so the number of headers follows from how many unbroken runs the month breaks into, not from how many receipts were rung up. The link rows are how you find out which receipts are inside which consolidated e-invoice. The Discrepancies Report drills into them when you open a consolidated line; the API exposes them at…/e-invoice/to-irb-match-gen-doc-hdr-links.
This is why the link table is several times bigger than the header table: it holds one row per receipt, where the header table holds one row per e-invoice.
There is a third link, written when the Batch Pool is consolidated: it ties each pool row to the consolidated header it went into. You do not normally read it; the Batch Pool screen uses it to show that a PROCESSED row went somewhere.
The chain, drawn from how the records point at each other
Each box below is a stage in the document’s life, in plain words. Every arrow means “holds the key of” (child points at parent). Omitted for legibility: the pool, batch pool and submission queue also hold the key of the document line, and the batch-pool line and cancellation queue also hold the key of the document; the intercompany and self-referencing document keys are out of scope. The submission history is drawn dashed because its reference to the header is not enforced.
flowchart LR
GD["Finalised document<br/>your invoice or cash bill"]
GDL["Document line"]
PQ["Entry gate at FINAL"]
BP["Batch Pool row<br/>waiting to be consolidated"]
IRB["LHDN copy — the e-invoice record<br/>frozen at creation, then LHDN's answers"]
IRBL["LHDN copy, one line per document line"]
LINK["Which documents are inside<br/>one row per e-invoice and document"]
BPL["Which pool rows went into which e-invoice"]
SQ["Submission queue<br/>a row exists until it is sent"]
CQ["Consolidation queue"]
VQ["Validation queue<br/>waiting for LHDN's verdict"]
HIST["Submission history<br/>what LHDN accepted, in monthly partitions"]
EM["E-mail thread to the buyer<br/>the Email Dashboard"]
RR["Rejection request — cancelling"]
CANQ["Cancellation queue"]
GDL --> GD
PQ --> GD
BP --> GD
IRB --> GD
IRBL --> IRB
IRBL --> GDL
LINK --> IRB
LINK --> GD
BPL --> BP
BPL --> IRB
SQ --> IRB
SQ --> GD
CQ --> IRB
VQ --> IRB
HIST -.-> IRB
EM --> IRB
RR --> IRB
RR --> GD
CANQ --> IRB
CANQ --> RR
Read it from the middle: everything that happens after the mandatory-field check hangs off the LHDN copy — the to-IRB header. The four queues (submission, consolidation, validation, cancellation) are transient — a row exists while the work is pending and is deleted or archived when it is done. The lines, the links, the history, the e-mail thread and the rejection request are permanent.
Statuses: three columns, one story
The header carries three status columns, and the moment a header is handed to the submission queue all three read IN_QUEUE. From there the applet’s processors move them: Submitted once LHDN has accepted the payload, then Valid or Invalid when the validation poll comes back, Cancelled after a rejection request succeeds. Consolidated headers start at IN_CONSOLIDATED_QUEUE instead. The full vocabulary and which processor writes each value are in the applet page’s lifecycle section.
For reading the screen, one rule is enough: IN_QUEUE means BigLedger has the record and LHDN has not seen it yet. It is not an error. If it is still IN_QUEUE tomorrow, open the submission queue row and read the error on it — nothing retries by itself.
The submission history: what was actually sent
A successful submission writes a submission history row — the request body as sent, the response as received, and the LHDN identifiers. A failed attempt writes no history row: it updates the submission queue (status SUBMISSION_FAILED, with the request and response details) and the header’s status, and stops there. So the history is the record of what LHDN accepted, not of every attempt — for a failed transmission, read the submission queue.
The table is partitioned by month on the row’s creation date. Partitions are created on demand — the first write in a month creates that month’s partition and every remaining month of the year — so you will see child tables named …_y2026m03 and so on. This is machinery, not something you manage, but it explains two things: why a database-level count of the parent looks different from the sum of its children on some tools, and why a month in which nothing was submitted has no partition at all.
Export files: the month in a file
Two exports live on the Export tab of To IRB E-Invoice, and one more on Submission History. Each produces a file that BigLedger keeps — the file itself in the platform file store, and a record of it on the tab it was exported from.
| Export | Format | What is in it |
|---|---|---|
| To IRB E-Invoice → Export → CSV | CSV | One row per to-IRB header matching your filter, 38 columns in a fixed order: the to-IRB running number and e-invoice datetime; your document numbers (Doc No, branch, second and third server numbers, company code, reference); process status and submission type; transaction date and amount; billing frequency and period; the five client document numbers; buyer name, identity number and TIN; supplier name, identity number and TIN; the LHDN submission and document identifiers; e-invoice version, type and code; the original e-invoice reference (for credit and debit notes); the LHDN unique reference number; and the two issue timestamps — BigLedger’s and LHDN’s |
| To IRB E-Invoice → Export → PDF | PDF (a zip) | The LHDN-format printable of each header in your filter, zipped |
| Submission History → Export | CSV | The same shape, from the history rows — every successful submission; failed attempts are in the submission queue, not the history |
Three things to know before you click:
- One at a time. Each export kind runs one job at a time per tenant. Ask for a second CSV while one is building and you get “Please wait until the current file finishes before generating additional files” — the first request is not lost, and the second is simply not queued. Wait for the row to show
DONE, then ask again. - A failed export is still a row. If the file could not be built, the row shows
FAILEDwith the reason in its error message. It does not disappear and it is not retried. - The CSV is the record as it is now. It reads the to-IRB header, so a header that was later cancelled shows
Cancelledtoday even if it wasValidwhen the month closed. For “what was true on the 7th”, export on the 7th and keep the file.
For the auditor’s request — every GSD invoice submitted in March with its LHDN reference — filter To IRB E-Invoice to company GSD and March, export CSV, and hand over the file. The unique reference number and validation link columns are what the auditor wants; the QR code is not in the CSV, it is on the PDF printable.
The discrepancy report: what “missing” means
Monthly Report → Discrepancies Report → Create takes a company and a date range and stores a report with one line per section (a section code, a document count, an amount and the list of document identifiers behind it). The tabs are described on the applet page; what that page does not spell out is the exact test behind each direction, and the exact test is what decides whether a line is a problem.
Exists in ERP, Missing in E-invoice. A document counts here when all of these hold: it is FINAL; it is not deleted; its transaction date is in the range; its document currency equals the company’s currency; and there is neither a to-IRB header pointing at it nor a match-link row naming it. Two things follow. Foreign-currency documents never appear on this side of the report — GSD’s USD purchases from a Singapore distributor are simply not examined, whatever their e-invoice state. And a document you marked Skip E-Invoice has no header and no link, so it does meet this test: the skipped documents are also totalled in their own bucket on the ERP Transaction Summary, but the discrepancy direction does not subtract them. A month in which twenty cash bills were skipped and are still final shows twenty “missing” documents that are not missing at all. A voided document, by contrast, is on neither list: both queries count final documents only. Read the skipped bucket first, then the discrepancy, and read it as a list of documents rather than a figure: it counts sales and purchase documents together, credit notes and returns included, at their stored amounts in the company’s own currency, so its total is not a revenue number.
Exists in E-invoice, Missing in ERP. A to-IRB header counts here when it has no document identifier of its own and no match-link rows — an e-invoice record that no longer points at anything. A healthy consolidated header has no document identifier but does have its links, so it is not caught; what this side finds is orphans, typically from a resubmission that replaced the links or a document deleted after submission. The same test is run again excluding consolidated headers, and a consolidated-only total is produced alongside, so you can tell whether consolidation explains the gap.
What the IRB Audit Summary counts, and which month it counts it in. The e-invoice side is selected by the header’s date, not the document’s, and sorted by status afterwards: a Cancelled header goes to the Cancelled figure, an Invalid header goes nowhere, and every other status — Valid, Submitted, IN_QUEUE, IN_CONSOLIDATED_QUEUE — is counted as Internal Submission. So that figure means “a record exists and has not been cancelled or rejected”, and a rejected e-invoice is absent from all three tabs: not in the summary, and not missing either, because its header points at the document. An individual header carries its document’s date; a consolidated header from the common run is dated the last day of the receipts’ month, which is why GS’s 38,000 March cash bills, consolidated on 4 April, still land in March on both sides. The per-type arithmetic that exposes a rejected e-invoice, and the two other things this report cannot see, are on the applet page.
For GadgetSphere’s March: the report for GSD shows 4 documents, RM 61,300, under Exists in ERP, Missing in E-invoice — the four invoices in the Individual Pool — and nothing in the other direction. That is the report working. The report for GS shows 0 in both directions, and the ERP Transaction Summary shows 38,000 cash bills of which 38,000 are in consolidated e-invoices. That is a closed month.
Procedure: trace one document from FINAL to LHDN
My E-Invoice Admin → Internal Submission → To IRB E-Invoice
A GSD customer says they never received the validated e-invoice for invoice GSD-INV-2603-0187, RM 18,400, dated 12 March. Under a minute, in this order:
1. Look at the document first. Open the invoice. If its To IRB reference is filled in, a record exists — go to step 2. If it is blank, there is no record: check the three pools for the invoice (it is RM 18,400 with a corporate buyer, so the Individual Pool is where a missing TIN would have parked it), and if it is in none of them, confirm the company’s e-invoice status was ENABLED when it was finalised — a document finalised while the switch was off is never queued and never will be.
2. Find the record. In To IRB E-Invoice, search Doc No GSD-INV-2603-0187. Read the status: Valid means LHDN accepted it and the validation link and QR are on the record — the customer’s PDF was e-mailed by the printable queue, and the Email Dashboard shows the thread; resend from there. Invalid means LHDN rejected it — the validation response on the record says why, and the fix is Save and Resubmit. IN_QUEUE means it has not been sent — open the submission queue row for the error.
3. If it is consolidated, find it inside. A GS cash bill will not have its own header. Its Doc No leads to a match-link row, and that row’s header is the consolidated e-invoice it was reported in — open the consolidated line in the Discrepancies Report drill-down, or query the link endpoint by document identifier. The customer gets the consolidated e-invoice’s reference, not one of their own; if they needed an individual e-invoice they had to ask for it at the till, before the month’s consolidation ran.
4. Read what was sent. Submission History, same Doc No. The request body is the exact payload; the response is LHDN’s exact answer. This is the row to quote when you open a ticket with LHDN or with BigLedger support.
5. Export if asked. Filter, Export tab, CSV or PDF, wait for DONE, download from the row.
What success looks like
Take yesterday’s largest individual invoice. In under a minute you can open its to-IRB record from Doc No, its status reads Valid, the validation link on the record opens LHDN’s page for it, and Submission History shows exactly one attempt. Take one branch’s consolidated e-invoice for last month: it carries no more than 200 lines, each line a consecutive range of receipt numbers, and the match-link rows behind it account for every receipt those ranges cover. Run the Discrepancies Report for last month: both discrepancy directions are zero, or every line on them is one you can name — the four invoices in the Individual Pool, the twenty skipped cash bills.
Common mistakes
| What people do | What actually happens | Do this instead |
|---|---|---|
| Fix the customer’s TIN and wait for the e-invoice to update | Nothing updates; the to-IRB record is a frozen copy | Save and Resubmit on the pool row or the to-IRB row |
| Filter the link or the API on the matching status | Always empty — the column is never written | Use the header’s status; the link only tells you which documents are inside |
| Treat an empty Exists in ERP, Missing in E-invoice as proof the month is complete | Foreign-currency documents were never examined | Check the foreign-currency documents separately, from the document listing |
| Treat every line under Exists in ERP, Missing in E-invoice as unreported revenue | Skipped documents meet the same test | Read the skipped bucket on the ERP Transaction Summary and subtract it |
| Click Export twice because nothing happened | The second request is refused with a message; the first is still building | Wait for the row to show DONE |
| Search Submission History for a document that is still in a pool | Nothing to find — history is written only when LHDN accepts a payload | Search the pools; the document has not reached the history yet |
| Search Submission History for a failed transmission | Nothing to find — a failed attempt updates the submission queue and writes no history row | Open the submission queue row; its status is SUBMISSION_FAILED and the request and response are on it |
| Search To IRB E-Invoice by the to-IRB running number, expecting the invoice number | They are different numbers | Search by Doc No |
Related documentation
- My E-Invoice Admin applet — every screen, every processor, every button, field by field
- Pools and queues — where a document goes when it cannot be sent
- Consolidated e-invoice — what a consolidated header contains, and which of the two consolidation runs carries a line cap
- Validation and clearance — what
Validmeans and how long it takes - The month-end e-invoice cycle — the 1st-to-7th routine that uses the exports and the discrepancy report
- Cancel and correct — the rejection request and resubmission paths