Skip to content
API Reference

The complete MyInvois Bridge integration API.

Everything an ERP, POS, or accounting integration needs: OAuth2 client credentials, invoice upload and status, webhooks, and the full error reference.

Overview

The MyInvoisBridge Integration API lets external systems such as ERP, POS, and accounting software push invoices programmatically. Invoices are validated and submitted to the Malaysian LHDN MyInvois platform automatically.

Base URL

https://app.myinvoisbridge.com

Version

v1 (stable)

Format

JSON (application/json)

Authentication

The API uses the OAuth 2.0 client credentials grant. Tenant admins create API clients in the API Integration page. Each client has a client_id and a one-time client_secret.

Flow

Create API clientExchange for token (1h)Call API with Bearer tokenRepeat on expiry

Token scopes

FieldTypeDescription
invoices:writescopeUpload invoices via POST /integration/invoices
invoices:readscopePoll invoice status via GET /integration/invoices/:documentId
webhooks:managescopeRegister, list, and delete webhook endpoints

Token endpoint

POST/api/v1/oauth/tokenrequires public endpoint

Exchange client credentials for an access token. The token endpoint is rate limited to 10 requests per minute per client ID.

Request body

FieldTypeDescription
grant_type*stringMust be "client_credentials"
client_id*stringYour mib_xxx client ID
client_secret*stringYour client secret shown once during creation or rotation

Response (200)

FieldTypeDescription
access_tokenstringJWT Bearer token
token_type"Bearer"Always "Bearer"
expires_innumberSeconds until expiry, typically 3600
scopestringSpace-separated list of granted scopes

Error codes

FieldTypeDescription
400 invalid_requesterrorMissing grant_type, client_id, or client_secret
401 invalid_clienterrorCredentials invalid or client not found
401 client_revokederrorClient exists but has been revoked
429 rate_limit_exceedederrorToo many requests. Check the Retry-After header.

Invoice upload

POST/api/v1/integration/invoicesrequires Bearer token with invoices:write

Upload an invoice for validation and asynchronous submission to LHDN. The API returns 202 Accepted immediately.

Required headers

FieldTypeDescription
Authorization*stringBearer {access_token}
Idempotency-Key*string1-256 characters. Reuse the same key only for safe retries of the same request.

Request body (schema v1)

FieldTypeDescription
schemaVersion*"v1"Must be "v1"
documentType*string"invoice" | "credit_note" | "debit_note"
documentNumber*stringUnique per company, for example "INV-2026-001"
issueDate*stringISO 8601 date in YYYY-MM-DD format
currency*stringISO 4217 currency code such as MYR
supplier*object{ tin: string, name: string, address?: string }
buyer*object{ tin?: string, name: string, address?: string, email?: string }
lines*arrayAt least one item. Each line includes description, quantity, unitPrice, and optional tax metadata.
totalExcludingTax*numberInvoice subtotal before tax
totalTaxAmount*numberTotal tax amount
totalIncludingTax*numberFinal grand total
notesstringOptional notes or remarks
referencestringOptional ERP or purchase order reference

Response (202)

FieldTypeDescription
documentIdstring (UUID)Use this to poll status or correlate webhook deliveries
status"processing"Initial status while validation and submission continue asynchronously

Error codes

FieldTypeDescription
400 missing_idempotency_keyerrorIdempotency-Key header was not provided
400 idempotency_key_invaliderrorIdempotency-Key must be between 1 and 256 characters
409 idempotency_key_conflicterrorThe same key was reused with a different request body
409 document_number_existserrorThe documentNumber has already been used for this company
422 validation_errorerrorThe request body failed schema validation

Invoice status

GET/api/v1/integration/invoices/:documentIdrequires Bearer token with invoices:read

Poll the status of a previously submitted invoice. A 404 indicates the document does not belong to your company or does not exist.

Response (200)

FieldTypeDescription
documentIdstring (UUID)The document identifier
documentNumberstringYour invoice number
statusstringSee status values below
lhdnSubmissionUidstring | nullLHDN submission UID after submission
lhdnUuidstring | nullLHDN document UUID after acceptance
failureReasonstring | nullSet for rejected or failed documents
updatedAtstring (ISO 8601)Last status update timestamp

Status values

FieldTypeDescription
processingstatusQueued for validation or LHDN submission
validatedstatusPassed local validation and is waiting for LHDN submission
submittedstatusSent to LHDN and awaiting the final result
acceptedstatusLHDN accepted the document. Terminal success state.
rejectedstatusLHDN rejected the document. Terminal failure state.
failedstatusValidation or submission error. Terminal failure state.

Webhooks

Instead of polling, you can register an HTTPS endpoint to receive signed POST requests when invoice status changes.

Register endpoint

POST/api/v1/integration/webhooksrequires Bearer token with webhooks:manage
FieldTypeDescription
url*stringHTTPS URL that can receive public webhook deliveries
events*string[]List of event names to subscribe to

List endpoints

GET/api/v1/integration/webhooksrequires Bearer token with webhooks:manage

Delete endpoint

DELETE/api/v1/integration/webhooks/:idrequires Bearer token with webhooks:manage

Available events

FieldTypeDescription
invoice.acceptedeventFired when LHDN accepts the invoice
invoice.rejectedeventFired when LHDN rejects the invoice
invoice.failedeventFired when local validation or submission fails

Payload shape

{
  "event": "invoice.accepted",
  "timestamp": 1750556400,
  "data": {
    "documentId": "uuid-here",
    "documentNumber": "INV-2026-001",
    "companyId": "uuid-here",
    "lhdnUuid": "...",
    "lhdnSubmissionUid": "..."
  }
}

Signature verification

Every delivery includes X-MyInvoisBridge-Signature in the format sha256=hex and X-MyInvoisBridge-Timestamp. Verify the signature using HMAC-SHA256 over the raw request body with your signing secret.

import { createHmac } from 'crypto';

function verifySignature(body, secret, signature) {
  const expected = 'sha256=' + createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return expected === signature;
}

Retry schedule for non-2xx responses: 1 minute, 5 minutes, 30 minutes, 2 hours, then permanent failure after five total attempts.

Error reference

All error responses follow this general shape:

{
  "statusCode": 422,
  "message": "Validation failed",
  "errors": { "documentNumber": ["must not be empty"] }
}

HTTP status codes

FieldTypeDescription
400statusBad request due to malformed input or missing required fields
401statusUnauthorized. Invalid or expired token, or invalid client credentials.
403statusForbidden. The caller is authenticated but lacks the required scope or permission.
404statusNot found. The resource does not exist or is not accessible to this company.
409statusConflict. Usually idempotency-key reuse with a different body or duplicate document numbers.
422statusValidation failure for request bodies that do not meet schema rules.
429statusRate limited. Check the Retry-After header before retrying.

Last updated · May 2026

Independent reference. MyInvois is operated by LHDN. We are not affiliated with LHDN.