Skip to main content

Malaysia MyInvois (LHDN) E-Invoicing

Technical nameecosire_einvoice_my
Odoo versions17.0, 18.0, 19.0 (Community or Enterprise)
Current shipped version19.0 -> 19.0.1.1.3, 18.0 -> 18.0.1.1.4, 17.0 -> 17.0.1.1.4
Price$599 USD, one-time (requires ecosire_einvoice_core)
Odoo module licenceOPL-1
CategoryAccounting / Localizations / EDI

This is the Malaysia country pack for the ECOSIRE e-invoicing core engine. It connects Odoo invoices and credit notes to MyInvois, the e-invoicing system operated by LHDN / IRBM (Lembaga Hasil Dalam Negeri Malaysia, the Inland Revenue Board of Malaysia).

Malaysia uses a direct government REST API: your ERP builds a digitally signed UBL 2.1 (MY) document and submits it straight to LHDN. LHDN validates it, stamps a UUID and a long ID, and notifies the buyer. There is no mandatory service provider in between.

The package is fully functional on installation: there is no separate licence module to install, no key to enter and no activation step. Use is governed by the OPL-1 licence terms.

Read before you buy: live submission needs digital signing

LHDN only accepts digitally signed documents (XAdES over the UBL 2.1 XML). This release does not sign documents yet. The exact signature form, canonicalization and accepted Certificate Authorities are still to be confirmed against the LHDN MyInvois SDK and your own taxpayer certificate, and signing against an unconfirmed scheme would make every submission fail.

What works today: the connection to LHDN (OAuth 2.0 token), company and partner identity fields, the UBL 2.1 (MY) document builder with its SHA-256 hash, and the submission records, audit log and scheduled actions. When you press Submit to LHDN (or the invoice's MyInvois button), the module builds the document and then stops with a clear message that signing is not yet enabled. Nothing is sent to LHDN and the submission is left unchanged. Plan your go-live date with this in mind.

Mandatory compliance timeline

Malaysia phased its e-invoice mandate by annual turnover: Phase 1 (August 2024, turnover above MYR 100M), Phase 2 (January 2025, MYR 25M to MYR 100M), Phase 3 (July 2025 onwards, the remaining taxpayers). Check current enforcement dates and any grace periods in the latest LHDN guidance at hasil.gov.my.

What it does​

  • OAuth 2.0 client-credentials connection to LHDN, for a taxpayer or for an intermediary acting on behalf of a taxpayer (onbehalfof TIN or TIN:ROB).
  • Sandbox and production environments with every API host held in configuration. Nothing is hardcoded.
  • UBL 2.1 (MY) document builder for invoice, credit note, debit note and refund note types (01 to 04) and the self-billed types (11 to 14). An invoice is sent as type 01, a customer credit note as 02 and a vendor refund as self-billed 12.
  • Pre-build checks: the supplier TIN, BRN and MSIC code must be set on the company and the invoice must carry a currency, or the document is not built.
  • General Public TIN (EI00000000010) used automatically when the buyer has no TIN (B2C).
  • Submission records with a state machine: Draft, Submitted, Cleared (Valid), Rejected/Invalid, Cancelled, Pending Retry.
  • Status polling every 15 minutes and an automatic retry every hour for submissions waiting to be retried (up to 5 attempts).
  • Cancel (issuer) and Reject (Buyer) actions on a submitted or cleared document.
  • LHDN validation link: once a document is valid, the module builds the MyInvois portal URL from the LHDN-stamped UUID and long ID. It never generates its own QR data.
  • Buyer TIN check against the LHDN taxpayer registry from the partner form.
  • Per-call audit log of every build, submit, poll, cancel and reject, and multi-company record rules on configuration and submissions.

Only the endpoints in the published MyInvois contract are implemented. The token endpoint is confirmed against the LHDN SDK; every document endpoint is taken from LHDN's published documentation and stays locked until you confirm it in the sandbox (see Sandbox Verified below).

Requirements​

RequirementDetail
Odoo17.0, 18.0 or 19.0 (Community or Enterprise)
Core engineecosire_einvoice_core installed (see the core engine page)
Python packageslxml, requests
LicenceNone required: no activation key, no separate licence module
LHDN accessA taxpayer TIN and MyInvois Portal access to register your ERP system and obtain a Client ID and Client Secret
Signing certificateA taxpayer digital certificate from an LHDN-accepted Certificate Authority, needed once document signing is enabled

Installation​

  1. Install the core engine first.
  2. Download this pack from the Odoo Apps Store after purchase and extract it into your addons directory:
    unzip ecosire_einvoice_my-*.zip -d /opt/odoo/addons/
    sudo systemctl restart odoo
  3. In Odoo go to Apps and click Update Apps List.
  4. Search for ECOSIRE E-Invoicing | Malaysia MyInvois (LHDN) and click Install.

After installation a MyInvois menu appears under Accounting, with Submissions and Configuration.

MenuWhat it is for
Accounting > MyInvois > SubmissionsEvery document submission, its LHDN identifiers, state and audit log
Accounting > MyInvois > ConfigurationOne connection record per company: environment, API hosts, credentials, intermediary mode, Sandbox Verified
Company form, MyInvois (Malaysia LHDN) sectionSupplier identity sent on every document
Partner form, MyInvois tabBuyer TIN, BRN, secondary ID and the Validate with LHDN button
Invoice form, MyInvois smart buttonCreates (or reuses) the submission and submits it

Configuration​

Step 1: Register your ERP system in the MyInvois Portal​

  1. Log in to the MyInvois Portal (preprod: preprod.myinvois.hasil.gov.my; production: myinvois.hasil.gov.my).
  2. Open the ERP System Registration section and register your Odoo instance to receive a Client ID and Client Secret.
  3. For intermediary mode, grant the intermediary's Client ID the required API permissions in the portal and note the taxpayer TIN (and ROB number if it applies).

Sandbox and production credentials are separate. Start in the preprod sandbox.

Step 2: Company identity​

Open Settings > Companies, select your company and fill in the MyInvois (Malaysia LHDN) section:

  • MyInvois TIN (myinvois_tin): your LHDN-issued Tax Identification Number
  • Business Registration No. (BRN) (myinvois_brn): your SSM business registration number
  • SST Registration No. (myinvois_sst_id): your Sales and Service Tax number, or NA if you are not SST-registered
  • MSIC Code (myinvois_msic_code): your 5-digit Malaysia Standard Industrial Classification code
  • Business Activity Description (myinvois_activity_desc): a short description of your main business activity

TIN, BRN and MSIC code are mandatory: a document is not built without them.

Step 3: The MyInvois connection​

  1. Go to Accounting > MyInvois > Configuration and create the record for your company (one per company).
  2. Set Environment to Sandbox (preprod) while testing.
  3. The sandbox and production API and token hosts are pre-filled with the LHDN defaults (https://preprod-api.myinvois.hasil.gov.my and https://api.myinvois.hasil.gov.my). Change them only if LHDN changes its hostnames.
  4. Enter the Client ID and Client Secret from the portal. Both are visible to system administrators only, the secret is a password field, and neither is written to the logs.
  5. Leave Scope as InvoicingAPI.
  6. For intermediary mode, tick Intermediary Mode and enter the taxpayer TIN (or TIN:ROB) in On-Behalf-Of.
  7. Click Test Connection. It requests a token from POST /connect/token without sending any document, and shows OK (token acquired) in Last Connection Test on success.

Step 4: Sandbox Verified​

Every document endpoint (submit, status, cancel/reject, TIN validation and the others) is locked behind the Sandbox Verified flag. Until it is ticked, these calls stop with a message explaining that the endpoint is not yet confirmed against the LHDN sandbox, so nothing can be sent to an unconfirmed URL.

Tick Sandbox Verified only after you have confirmed the document endpoints against the LHDN preprod sandbox, and switch Environment to Production only after that.

Daily usage​

Partner TIN check​

  1. Open the buyer's partner record and go to the MyInvois tab.
  2. Enter the buyer's MyInvois TIN and a secondary identifier: MyInvois ID Type (NRIC, Passport, BRN or Army) and MyInvois ID Value. If no ID value is set, the BRN is used.
  3. Click Validate with LHDN. The module calls GET /api/v1.0/taxpayer/validate/{tin} with the ID type and value, and ticks TIN Validated when LHDN confirms the match.

A buyer without a TIN is invoiced with the General Public TIN EI00000000010.

Building and submitting a document​

  1. Confirm a customer invoice or credit note (or a vendor refund) as usual.
  2. Click the MyInvois smart button on the invoice. The module creates a submission record linked to the invoice, builds the UBL 2.1 (MY) XML and its SHA-256 hash, then submits it.
  3. You can also work from Accounting > MyInvois > Submissions: Build Document builds the XML and hash locally without sending anything, and Submit to LHDN sends it.

In this release the submit step stops at signing (see the note at the top of this page): Odoo shows the signing message and nothing is sent. To review the XML before go-live, create the submission from Submissions and use Build Document. Once signing is enabled, an accepted document moves to Submitted with its LHDN Submission UID and Document UUID, and a submission whose call fails moves to Pending Retry.

Status, cancel and reject​

  • Poll Status (or the 15-minute scheduled action) reads GET /api/v1.0/documentsubmissions/{submissionUid} and moves the submission to Cleared (Valid), Rejected/Invalid or Cancelled. A valid document receives its Long ID and the LHDN validation URL.
  • Cancel sends the issuer's cancellation to PUT /api/v1.0/documents/state/{uuid}/state. LHDN allows cancellation within 72 hours of validation.
  • Reject (Buyer) sends a buyer rejection to the same endpoint, within LHDN's 72-hour window.
  • Submissions in Pending Retry are retried every hour, up to 5 attempts.

Audit log​

Every submission keeps its own audit log (build, submit, poll, cancel, reject, validate) with the result and message of each call. Use it first when you need to know what was sent and what LHDN answered.

API details​

FieldValue
Auth methodOAuth 2.0 client credentials (confirmed)
Token endpoint (sandbox)https://preprod-api.myinvois.hasil.gov.my/connect/token
Token endpoint (production)https://api.myinvois.hasil.gov.my/connect/token
Document submissionPOST /api/v1.0/documentsubmissions (at most 100 documents per call)
Submission statusGET /api/v1.0/documentsubmissions/{submissionUid}
Document details / rawGET /api/v1.0/documents/{uuid}/details, GET /api/v1.0/documents/{uuid}/raw
Cancel / rejectPUT /api/v1.0/documents/state/{uuid}/state
TIN validationGET /api/v1.0/taxpayer/validate/{tin}?idType=...&idValue=...
Recent documents, notifications, document types/api/v1.0/documents/recent, /api/v1.0/notifications/taxpayer, /api/v1.0/documenttypes
Endpoint statusToken endpoint confirmed; all others locked until Sandbox Verified

Troubleshooting​

IssueSolution
"MyInvois XAdES digital signing is not yet enabled"Expected in this release: document signing is not yet available, so nothing is sent. See the note at the top of this page
"...uses an endpoint that is REPORTED..."Tick Sandbox Verified on the configuration once you have confirmed the endpoints against the LHDN sandbox
Test Connection failsCheck the Client ID and Client Secret against the MyInvois Portal and that Environment points at the matching sandbox or production host
"Cannot build MyInvois document"Fill in the company TIN, BRN and MSIC code, and make sure the invoice has a currency
"No MyInvois configuration found for company"Create the configuration under Accounting > MyInvois > Configuration for the active company
Validate with LHDN asks for an ID valueEnter a MyInvois ID Value (or the BRN) on the partner, with its MyInvois ID Type
Validation URL empty on a submissionIt is set only after LHDN marks the document Valid and returns its long ID

Release notes​

1.1.3 (Odoo 19) and 1.1.4 (Odoo 18 and 17): installs on its own​

The package no longer asks for a separate ECOSIRE licence module. Earlier downloads could not be installed on a fresh Odoo because they depended on a module that was never part of the package. All references to it have been removed from the module and its tests, there is no activation step, and a test now checks on every build that the package is self-contained.

Upgrade to 19.0.1.1.3, 18.0.1.1.4 or 17.0.1.1.4. No configuration changes are needed and your submissions and audit log are untouched.

Earlier​

  • 1.1.2: one failing record in a scheduled poll or retry no longer stops the rest of the run.
  • 18.0 / 17.0 1.1.3: the App Store price was aligned with ecosire.com at $599 USD.

Support​