e-Invoice field validator for Malaysian SMEs

Check every field of an e-Invoice XML against the 49 LHDN table rules. Each line names the rule, its UBL path and what the file shows instead. Never a verdict.

🔒 100% client-side · zero server upload

Malaysian rules & edge cases

Every rule this tool checks comes from one place: the LHDN SDK page's Data Structure tables for Invoice v1.0, read on 2026-11-08 — the Mandatory column, the Number of Chars column and the Cardinality column for each element. The tool does not invent rules and it does not add rules of its own. Where the table says “Required” or “Mandatory where applicable”, the wording here follows the table: a required field is absent and the line says so; a conditional field whose trigger is not visible in the file is reported as not evaluated, never as a pass.

What is checkedHow the table says it
e-Invoice Type Code, Invoice number, Issue date and Issue time, Invoice currencyEach is 1-1 and mandatory; the type code is 2 characters and must be one of the published e-invoice types (01, 02, …).
Supplier name (300 characters), supplier TIN (14), MSIC code (5), address, phoneEach mandatory, with the table's own length limits; the phone number is 20 characters at most.
Tax subtotals: Tax Amount, Taxable Amount, Tax Category IDTax Amount and Tax Category ID are mandatory in every subtotal; Taxable Amount is conditional on a tax exemption (type E).
Each line: description (300 characters), classification, unit price, tax amount, subtotal, quantityAll mandatory per line except quantity; a description longer than 300 characters shows the limit and the actual length.
Buyer: name, TIN, registration number, address, phoneEach mandatory, with the table's own length limits (buyer TIN 14, address line 150).
Supplier SST registration number, Currency Exchange Rate, exemption detailsConditional on the trigger the table names: a type 02 subtotal, a non-MYR currency, an exemption subtotal. No visible trigger, no evaluation.
  • Never a verdict. A finding says “the table marks this Mandatory and it is absent from this file”. It never says “LHDN will reject this” and it never computes an acceptance outcome.
  • What this does not check is listed on the page and kept there after every run: signature validity, arithmetic between the amounts, whether the issue date is today in UTC, MSIC/classification codes against the full LHDN code lists (only the two code lists shipped here are checked), and the buyer's tax residency.
  • Money, dates and numbers are never parsed or recomputed. The validator reads presence, length and code values only, so no amount in the file is ever added, compared or rounded here.
  • Elements are matched by local name. A document that uses Cbc:, cbc: or any other prefix for the same namespace reads the same way.
  • Party identifiers are told apart by schemeID. The table lists three rules that all point at PartyIdentification/ID; the written TIN, BRN and SST each carry their own schemeID, and each rule reads only its own.
  • Every rule is read from the table itself, and the read is recorded on this page with its date, exactly like a rate table. When the table changes, the rules are re-read; nothing on this page asserts a compliance outcome about a file.

Nothing you paste leaves your browser: the page is static JavaScript with no network calls.

Before and after

Before

<?xml version="1.0" encoding="UTF-8"?>
<ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2.1"
  xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
  xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
  <cbc:ID>INV20261108002</cbc:ID>
  <cbc:IssueDate>2026-11-08</cbc:IssueDate>
  <cbc:IssueDate>2026-11-08</cbc:IssueDate>
  <cbc:IssueTime>09:15:00</cbc:IssueTime>
  <cbc:InvoiceTypeCode listVersionID="1.0">01</cbc:InvoiceTypeCode>
  <cbc:DocumentCurrencyCode>MYR</cbc:DocumentCurrencyCode>
  <cbc:TaxCurrencyCode>MYR</cbc:TaxCurrencyCode>
  <cac:Signature>
    <cbc:ID>sig-001</cbc:ID>
  </cac:Signature>
  <cac:AccountingSupplierParty>
    <cac:Party>
      <cac:PartyIdentification>
        <cbc:ID schemeID="BRN">202501123456</cbc:ID>
      </cac:PartyIdentification>
      <cac:PartyIdentification>
        <cbc:ID schemeID="SST">B16-1234567890</cbc:ID>
      </cac:PartyIdentification>
      <cbc:IndustryClassificationCode name="Computer programming activities" schemeID="MSIC">62012</cbc:IndustryClassificationCode>
      <cac:PostalAddress>
        <cac:AddressLine>
          <cbc:Line>12, Jalan Teknologi 3/1, Taman Sains</cbc:Line>
        </cac:AddressLine>
        <cbc:CityName>Cyberjaya</cbc:CityName>
        <cbc:PostalZone>63000</cbc:PostalZone>
        <cbc:CountrySubentityCode>10</cbc:CountrySubentityCode>
        <cac:Country>
          <cbc:IdentificationCode>MYS</cbc:IdentificationCode>
        </cac:Country>
      </cac:PostalAddress>
      <cac:Contact>
        <cbc:Telephone>+603-8311 0000</cbc:Telephone>
        <cbc:ElectronicMail>billing@hoelee.com</cbc:ElectronicMail>
      </cac:Contact>
      <cac:PartyLegalEntity>
        <cbc:RegistrationName>Hoelee Enterprise</cbc:RegistrationName>
      </cac:PartyLegalEntity>
    </cac:Party>
  </cac:AccountingSupplierParty>
  <cac:AccountingCustomerParty>
    <cac:Party>
      <cac:PartyIdentification>
        <cbc:ID schemeID="TIN">C87654321090</cbc:ID>
      </cac:PartyIdentification>
      <cac:PartyLegalEntity>
        <cbc:RegistrationName>Restoran Aisyah Sdn. Bhd.</cbc:RegistrationName>
      </cac:PartyLegalEntity>
      <cac:PostalAddress>
        <cac:AddressLine>
          <cbc:Line>8, Jalan Persiaran Merdeka</cbc:Line>
        </cac:AddressLine>
        <cbc:CityName>Shah Alam</cbc:CityName>
        <cbc:CountrySubentityCode>10</cbc:CountrySubentityCode>
        <cac:Country>
          <cbc:IdentificationCode>MYS</cbc:IdentificationCode>
        </cac:Country>
      </cac:PostalAddress>
      <cac:Contact>
        <cbc:Telephone>+603-5511 2222</cbc:Telephone>
      </cac:Contact>
    </cac:Party>
  </cac:AccountingCustomerParty>
  <cac:TaxTotal>
    <cbc:TaxAmount currencyID="MYR">37.40</cbc:TaxAmount>
    <cac:TaxSubtotal>
      <cbc:TaxableAmount currencyID="MYR">400.00</cbc:TaxableAmount>
      <cbc:TaxAmount currencyID="MYR">32.00</cbc:TaxAmount>
      <cac:TaxCategory>
        <cbc:ID>02</cbc:ID>
        <cbc:Percent>8.00</cbc:Percent>
      </cac:TaxCategory>
    </cac:TaxSubtotal>
    <cac:TaxSubtotal>
      <cac:TaxCategory>
        <cbc:ID>99</cbc:ID>
        <cbc:Percent>10.00</cbc:Percent>
      </cac:TaxCategory>
    </cac:TaxSubtotal>
  </cac:TaxTotal>
  <cac:LegalMonetaryTotal>
    <cbc:LineExtensionAmount currencyID="MYR">454.00</cbc:LineExtensionAmount>
    <cbc:TaxExclusiveAmount currencyID="MYR">454.00</cbc:TaxExclusiveAmount>
    <cbc:TaxInclusiveAmount currencyID="MYR">491.40</cbc:TaxInclusiveAmount>
    <cbc:PayableAmount currencyID="MYR">491.40</cbc:PayableAmount>
  </cac:LegalMonetaryTotal>
  <cac:InvoiceLine>
    <cbc:ID>1</cbc:ID>
    <cbc:InvoicedQuantity unitCode="EA">1</cbc:InvoicedQuantity>
    <cbc:LineExtensionAmount currencyID="MYR">400.00</cbc:LineExtensionAmount>
    <cac:TaxTotal>
      <cbc:TaxAmount currencyID="MYR">32.00</cbc:TaxAmount>
      <cac:TaxSubtotal>
        <cbc:TaxableAmount currencyID="MYR">400.00</cbc:TaxableAmount>
        <cbc:TaxAmount currencyID="MYR">32.00</cbc:TaxAmount>
        <cac:TaxCategory>
          <cbc:ID>02</cbc:ID>
          <cbc:Percent>8.00</cbc:Percent>
        </cac:TaxCategory>
      </cac:TaxSubtotal>
    </cac:TaxTotal>
    <cac:Item>
      <cbc:Description>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</cbc:Description>
      <cac:CommodityClassification>
        <cbc:ItemClassificationCode listID="CLASS">T10</cbc:ItemClassificationCode>
      </cac:CommodityClassification>
    </cac:Item>
    <cac:Price>
      <cbc:PriceAmount currencyID="MYR">400.00</cbc:PriceAmount>
    </cac:Price>
  </cac:InvoiceLine>
  <cac:InvoiceLine>
    <cbc:ID>2</cbc:ID>
    <cbc:InvoicedQuantity unitCode="EA">6</cbc:InvoicedQuantity>
    <cbc:LineExtensionAmount currencyID="MYR">54.00</cbc:LineExtensionAmount>
    <cac:TaxTotal>
      <cbc:TaxAmount currencyID="MYR">5.40</cbc:TaxAmount>
      <cac:TaxSubtotal>
        <cbc:TaxableAmount currencyID="MYR">54.00</cbc:TaxableAmount>
        <cbc:TaxAmount currencyID="MYR">5.40</cbc:TaxAmount>
        <cac:TaxCategory>
          <cbc:ID>01</cbc:ID>
          <cbc:Percent>10.00</cbc:Percent>
        </cac:TaxCategory>
      </cac:TaxSubtotal>
    </cac:TaxTotal>
    <cac:Item>
      <cbc:Description>IP camera 4MP — wired</cbc:Description>
      <cac:CommodityClassification>
        <cbc:ItemClassificationCode listID="CLASS">P10</cbc:ItemClassificationCode>
      </cac:CommodityClassification>
    </cac:Item>
    <cac:Price>
      <cbc:PriceAmount currencyID="MYR">10.00</cbc:PriceAmount>
    </cac:Price>
  </cac:InvoiceLine>
</ubl:Invoice>

After

<?xml version="1.0" encoding="UTF-8"?>
<ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2.1"
  xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
  xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
  <cbc:ID>INV20261108003</cbc:ID>
  <cbc:IssueDate>2026-11-08</cbc:IssueDate>
  <cbc:IssueTime>09:15:00</cbc:IssueTime>
  <cbc:InvoiceTypeCode listVersionID="1.0">01</cbc:InvoiceTypeCode>
  <cbc:DocumentCurrencyCode>MYR</cbc:DocumentCurrencyCode>
  <cbc:TaxCurrencyCode>MYR</cbc:TaxCurrencyCode>
  <cac:Signature>
    <cbc:ID>sig-001</cbc:ID>
  </cac:Signature>
  <cac:AccountingSupplierParty>
    <cac:Party>
      <cac:PartyIdentification>
        <cbc:ID schemeID="TIN">C12345678901</cbc:ID>
      </cac:PartyIdentification>
      <cac:PartyIdentification>
        <cbc:ID schemeID="BRN">202501123456</cbc:ID>
      </cac:PartyIdentification>
      <cac:PartyIdentification>
        <cbc:ID schemeID="SST">B16-1234567890</cbc:ID>
      </cac:PartyIdentification>
      <cbc:IndustryClassificationCode name="Computer programming activities" schemeID="MSIC">62012</cbc:IndustryClassificationCode>
      <cac:PostalAddress>
        <cac:AddressLine>
          <cbc:Line>12, Jalan Teknologi 3/1, Taman Sains</cbc:Line>
        </cac:AddressLine>
        <cbc:CityName>Cyberjaya</cbc:CityName>
        <cbc:PostalZone>63000</cbc:PostalZone>
        <cbc:CountrySubentityCode>10</cbc:CountrySubentityCode>
        <cac:Country>
          <cbc:IdentificationCode>MYS</cbc:IdentificationCode>
        </cac:Country>
      </cac:PostalAddress>
      <cac:Contact>
        <cbc:Telephone>+603-8311 0000</cbc:Telephone>
        <cbc:ElectronicMail>billing@hoelee.com</cbc:ElectronicMail>
      </cac:Contact>
      <cac:PartyLegalEntity>
        <cbc:RegistrationName>Hoelee Enterprise</cbc:RegistrationName>
      </cac:PartyLegalEntity>
    </cac:Party>
  </cac:AccountingSupplierParty>
  <cac:AccountingCustomerParty>
    <cac:Party>
      <cac:PartyIdentification>
        <cbc:ID schemeID="TIN">C87654321090</cbc:ID>
      </cac:PartyIdentification>
      <cac:PartyIdentification>
        <cbc:ID schemeID="BRN">202901234567</cbc:ID>
      </cac:PartyIdentification>
      <cac:PartyLegalEntity>
        <cbc:RegistrationName>Restoran Aisyah Sdn. Bhd.</cbc:RegistrationName>
      </cac:PartyLegalEntity>
      <cac:PostalAddress>
        <cac:AddressLine>
          <cbc:Line>8, Jalan Persiaran Merdeka</cbc:Line>
        </cac:AddressLine>
        <cbc:CityName>Shah Alam</cbc:CityName>
        <cbc:CountrySubentityCode>10</cbc:CountrySubentityCode>
        <cac:Country>
          <cbc:IdentificationCode>MYS</cbc:IdentificationCode>
        </cac:Country>
      </cac:PostalAddress>
      <cac:Contact>
        <cbc:Telephone>+603-5511 2222</cbc:Telephone>
      </cac:Contact>
    </cac:Party>
  </cac:AccountingCustomerParty>
  <cac:TaxTotal>
    <cbc:TaxAmount currencyID="MYR">37.40</cbc:TaxAmount>
    <cac:TaxSubtotal>
      <cbc:TaxableAmount currencyID="MYR">400.00</cbc:TaxableAmount>
      <cbc:TaxAmount currencyID="MYR">32.00</cbc:TaxAmount>
      <cac:TaxCategory>
        <cbc:ID>02</cbc:ID>
        <cbc:Percent>8.00</cbc:Percent>
      </cac:TaxCategory>
    </cac:TaxSubtotal>
    <cac:TaxSubtotal>
      <cbc:TaxableAmount currencyID="MYR">54.00</cbc:TaxableAmount>
      <cbc:TaxAmount currencyID="MYR">5.40</cbc:TaxAmount>
      <cac:TaxCategory>
        <cbc:ID>01</cbc:ID>
        <cbc:Percent>10.00</cbc:Percent>
      </cac:TaxCategory>
    </cac:TaxSubtotal>
  </cac:TaxTotal>
  <cac:LegalMonetaryTotal>
    <cbc:LineExtensionAmount currencyID="MYR">454.00</cbc:LineExtensionAmount>
    <cbc:TaxExclusiveAmount currencyID="MYR">454.00</cbc:TaxExclusiveAmount>
    <cbc:TaxInclusiveAmount currencyID="MYR">491.40</cbc:TaxInclusiveAmount>
    <cbc:PayableAmount currencyID="MYR">491.40</cbc:PayableAmount>
  </cac:LegalMonetaryTotal>
  <cac:InvoiceLine>
    <cbc:ID>1</cbc:ID>
    <cbc:InvoicedQuantity unitCode="EA">1</cbc:InvoicedQuantity>
    <cbc:LineExtensionAmount currencyID="MYR">400.00</cbc:LineExtensionAmount>
    <cac:TaxTotal>
      <cbc:TaxAmount currencyID="MYR">32.00</cbc:TaxAmount>
      <cac:TaxSubtotal>
        <cbc:TaxableAmount currencyID="MYR">400.00</cbc:TaxableAmount>
        <cbc:TaxAmount currencyID="MYR">32.00</cbc:TaxAmount>
        <cac:TaxCategory>
          <cbc:ID>02</cbc:ID>
          <cbc:Percent>8.00</cbc:Percent>
        </cac:TaxCategory>
      </cac:TaxSubtotal>
    </cac:TaxTotal>
    <cac:Item>
      <cbc:Description>Website maintenance service — November 2026</cbc:Description>
      <cac:CommodityClassification>
        <cbc:ItemClassificationCode listID="CLASS">T10</cbc:ItemClassificationCode>
      </cac:CommodityClassification>
    </cac:Item>
    <cac:ItemPriceExtension>
      <cbc:Amount currencyID="MYR">400.00</cbc:Amount>
    </cac:ItemPriceExtension>
    <cac:Price>
      <cbc:PriceAmount currencyID="MYR">400.00</cbc:PriceAmount>
    </cac:Price>
  </cac:InvoiceLine>
  <cac:InvoiceLine>
    <cbc:ID>2</cbc:ID>
    <cbc:InvoicedQuantity unitCode="EA">6</cbc:InvoicedQuantity>
    <cbc:LineExtensionAmount currencyID="MYR">54.00</cbc:LineExtensionAmount>
    <cac:TaxTotal>
      <cbc:TaxAmount currencyID="MYR">5.40</cbc:TaxAmount>
      <cac:TaxSubtotal>
        <cbc:TaxableAmount currencyID="MYR">54.00</cbc:TaxableAmount>
        <cbc:TaxAmount currencyID="MYR">5.40</cbc:TaxAmount>
        <cac:TaxCategory>
          <cbc:ID>01</cbc:ID>
          <cbc:Percent>10.00</cbc:Percent>
        </cac:TaxCategory>
      </cac:TaxSubtotal>
    </cac:TaxTotal>
    <cac:Item>
      <cbc:Description>IP camera 4MP — wired</cbc:Description>
      <cac:CommodityClassification>
        <cbc:ItemClassificationCode listID="CLASS">P10</cbc:ItemClassificationCode>
      </cac:CommodityClassification>
    </cac:Item>
    <cac:ItemPriceExtension>
      <cbc:Amount currencyID="MYR">54.00</cbc:Amount>
    </cac:ItemPriceExtension>
    <cac:Price>
      <cbc:PriceAmount currencyID="MYR">10.00</cbc:PriceAmount>
    </cac:Price>
  </cac:InvoiceLine>
</ubl:Invoice>

How it works

  1. Copy the XML from your accounting system and paste it into the Input box. The sample below already shows a Malaysia invoice with six faults, one of each kind.
  2. Press Run. The Output box lists every rule the file does not satisfy, each naming the rule, the UBL path, the table own element name and what the file has instead — then the rules that were not evaluated because their trigger is not visible, then what this tool does not check. Tick (Show satisfied rules) to see the rules the file passes.
  3. Copy result if you want the findings elsewhere. The file itself is not changed and nothing is uploaded; open the Network tab and you will see zero requests.

FAQ

Does this tool tell me whether LHDN will accept my invoice?

No. It never says a document is accepted or rejected and it does not compute an acceptance outcome. Every finding is a statement about the file and the published table together — the table marks this Mandatory and it is absent from this file — and the What this does not check list is printed with every run.

Why are some rules “not evaluated” instead of passing?

Four rules are conditional: they only apply when their trigger is visible in the file — a non-MYR document currency, a tax exemption (type E), a service tax charge (type 02). When the trigger is not in the file, the engine cannot tell whether the rule applies, so it says “not evaluated” with the trigger text, rather than pretending the rule passed.

Where do the 49 rules come from?

Each row is transcribed from the LHDN SDK page’s Data Structure tables for Invoice v1.0, read on 2026-11-08 — the Mandatory column, the Number of Chars column and the Cardinality column. The rules module names the element, the chain, the requirement and the trigger in the table’s own words; a source-of-truth read date is recorded with the module, exactly like a rate table.

Does it check that my amounts add up?

No. The engine reads presence, length and code values only. Money, dates and numbers are never parsed or recomputed, so no amount is ever added, compared or rounded here — arithmetic between the amounts is on the “What this does not check” list.

Is my invoice or my buyer’s TIN uploaded anywhere?

No. The validator is static JavaScript in your browser: open the Network tab and you will see zero requests. An invoice carries a real business registration number and a real TIN, and neither leaves the machine.