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
Token scopes
| Field | Type | Description |
|---|---|---|
invoices:write | scope | Upload invoices via POST /integration/invoices |
invoices:read | scope | Poll invoice status via GET /integration/invoices/:documentId |
webhooks:manage | scope | Register, list, and delete webhook endpoints |
Token endpoint
/api/v1/oauth/tokenrequires public endpointExchange client credentials for an access token. The token endpoint is rate limited to 10 requests per minute per client ID.
Request body
| Field | Type | Description |
|---|---|---|
grant_type* | string | Must be "client_credentials" |
client_id* | string | Your mib_xxx client ID |
client_secret* | string | Your client secret shown once during creation or rotation |
Response (200)
| Field | Type | Description |
|---|---|---|
access_token | string | JWT Bearer token |
token_type | "Bearer" | Always "Bearer" |
expires_in | number | Seconds until expiry, typically 3600 |
scope | string | Space-separated list of granted scopes |
Error codes
| Field | Type | Description |
|---|---|---|
400 invalid_request | error | Missing grant_type, client_id, or client_secret |
401 invalid_client | error | Credentials invalid or client not found |
401 client_revoked | error | Client exists but has been revoked |
429 rate_limit_exceeded | error | Too many requests. Check the Retry-After header. |
Invoice upload
/api/v1/integration/invoicesrequires Bearer token with invoices:writeUpload an invoice for validation and asynchronous submission to LHDN. The API returns 202 Accepted immediately.
Required headers
| Field | Type | Description |
|---|---|---|
Authorization* | string | Bearer {access_token} |
Idempotency-Key* | string | 1-256 characters. Reuse the same key only for safe retries of the same request. |
Request body (schema v1)
| Field | Type | Description |
|---|---|---|
schemaVersion* | "v1" | Must be "v1" |
documentType* | string | "invoice" | "credit_note" | "debit_note" |
documentNumber* | string | Unique per company, for example "INV-2026-001" |
issueDate* | string | ISO 8601 date in YYYY-MM-DD format |
currency* | string | ISO 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* | array | At least one item. Each line includes description, quantity, unitPrice, and optional tax metadata. |
totalExcludingTax* | number | Invoice subtotal before tax |
totalTaxAmount* | number | Total tax amount |
totalIncludingTax* | number | Final grand total |
notes | string | Optional notes or remarks |
reference | string | Optional ERP or purchase order reference |
Response (202)
| Field | Type | Description |
|---|---|---|
documentId | string (UUID) | Use this to poll status or correlate webhook deliveries |
status | "processing" | Initial status while validation and submission continue asynchronously |
Error codes
| Field | Type | Description |
|---|---|---|
400 missing_idempotency_key | error | Idempotency-Key header was not provided |
400 idempotency_key_invalid | error | Idempotency-Key must be between 1 and 256 characters |
409 idempotency_key_conflict | error | The same key was reused with a different request body |
409 document_number_exists | error | The documentNumber has already been used for this company |
422 validation_error | error | The request body failed schema validation |
Invoice status
/api/v1/integration/invoices/:documentIdrequires Bearer token with invoices:readPoll the status of a previously submitted invoice. A 404 indicates the document does not belong to your company or does not exist.
Response (200)
| Field | Type | Description |
|---|---|---|
documentId | string (UUID) | The document identifier |
documentNumber | string | Your invoice number |
status | string | See status values below |
lhdnSubmissionUid | string | null | LHDN submission UID after submission |
lhdnUuid | string | null | LHDN document UUID after acceptance |
failureReason | string | null | Set for rejected or failed documents |
updatedAt | string (ISO 8601) | Last status update timestamp |
Status values
| Field | Type | Description |
|---|---|---|
processing | status | Queued for validation or LHDN submission |
validated | status | Passed local validation and is waiting for LHDN submission |
submitted | status | Sent to LHDN and awaiting the final result |
accepted | status | LHDN accepted the document. Terminal success state. |
rejected | status | LHDN rejected the document. Terminal failure state. |
failed | status | Validation 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
/api/v1/integration/webhooksrequires Bearer token with webhooks:manage| Field | Type | Description |
|---|---|---|
url* | string | HTTPS URL that can receive public webhook deliveries |
events* | string[] | List of event names to subscribe to |
List endpoints
/api/v1/integration/webhooksrequires Bearer token with webhooks:manageDelete endpoint
/api/v1/integration/webhooks/:idrequires Bearer token with webhooks:manageAvailable events
| Field | Type | Description |
|---|---|---|
invoice.accepted | event | Fired when LHDN accepts the invoice |
invoice.rejected | event | Fired when LHDN rejects the invoice |
invoice.failed | event | Fired 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
| Field | Type | Description |
|---|---|---|
400 | status | Bad request due to malformed input or missing required fields |
401 | status | Unauthorized. Invalid or expired token, or invalid client credentials. |
403 | status | Forbidden. The caller is authenticated but lacks the required scope or permission. |
404 | status | Not found. The resource does not exist or is not accessible to this company. |
409 | status | Conflict. Usually idempotency-key reuse with a different body or duplicate document numbers. |
422 | status | Validation failure for request bodies that do not meet schema rules. |
429 | status | Rate 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.