API overview
The Invoa REST API for developers: base URL, JSON conventions, money in integer sen, dates, request IDs, a first request and every resource.
The Invoa REST API lets your own systems create contacts, draft and issue invoices, record payments, run billing schedules and submit e-invoices to LHDN. Use it to connect a booking system, an online shop or your ERP to Invoa.
The REST API and webhooks are part of the Core and Pro plans. On Free, requests return 403 plan_limit.
Each API key belongs to one company in Invoa. Every request reads and writes that company's data only.
Base URL
https://app.invoa.io/api/v1All requests use HTTPS. Send and receive JSON (content-type: application/json), except the PDF download, which returns application/pdf.
Conventions
| Topic | Rule |
|---|---|
| Field names | camelCase in requests and responses, for example contactId, unitPrice, issueDate. The exceptions are has_more in the pagination object and required_scope / required_scopes in insufficient_scope error details. |
| Money | Integers in sen. 125000 is RM 1,250.00. Never send decimals or strings for amounts. |
| Currency | MYR unless you set currency on a document. |
| Dates | Business dates are YYYY-MM-DD, for example "2026-10-15". If you leave a date out, Invoa uses today in the company's timezone (default Asia/Kuala_Lumpur). |
| Timestamps | ISO 8601 in UTC, for example "2026-10-15T02:14:09.000Z". |
| Decimal quantities and rates | Fields stored as decimals come back as strings: quantity ("1.5000"), taxRate ("8.00"), discountValue, exchangeRate. Send quantity as a number. |
| IDs | UUIDs. |
| Responses | A single object is wrapped in data. Lists return data plus pagination. Errors return error. |
Request IDs
Every authenticated response carries an x-request-id header. Send your own x-request-id on a request and Invoa echoes it back; otherwise it generates one. Include it when you contact support about a specific call.
Your first request
Create an API key in Settings → API keys (see Authentication and scopes), then list your contacts:
curl https://app.invoa.io/api/v1/contacts?limit=2 \
-H "Authorization: Bearer $INVOA_API_KEY"{
"data": [
{
"id": "c0a10000-0000-4000-8000-000000000007",
"kind": "customer",
"name": "Aina Rahman",
"companyName": "Teratai Biru Ventures Sdn. Bhd.",
"tin": "C12345678900",
"email": "aina@teratai-biru.example",
"outstanding": 135000,
"documentCount": 4
}
],
"pagination": { "total": 1, "limit": 2, "offset": 0, "has_more": false }
}(Contact objects have more fields than shown; see Contacts.)
A typical flow
- Find or create the customer:
GET /contacts?search=…, thenPOST /contacts. - Create a draft invoice with an
idempotencyKey:POST /documents. - Issue it to allocate the number and create the PDF:
POST /documents/{id}/issue. - Email it to the customer:
POST /documents/{id}/send. - When the money arrives, record the payment, which issues a receipt:
POST /payments. - Optionally submit the invoice to LHDN:
POST /einvoice/{documentId}/submit. - Listen for webhooks instead of polling.
Note: API keys are trusted system integrations. Unlike Claude, an API key with the right scope issues, voids and submits immediately, without a human approval step.
Resources
| Resource | Endpoints |
|---|---|
| Contacts | List, retrieve with balances, create, update customers and suppliers. |
| Items | List and create catalogue items. |
| Documents | List, retrieve, create drafts, issue, send, download the PDF, void. |
| Payments | List payments, record a payment and issue a receipt. |
| Billing schedules | List, retrieve, create, trigger a milestone. |
| e-Invoice | Submit to LHDN MyInvois, check status. |
| Webhooks | Manage endpoints, events, payloads and signature verification. |
There is no endpoint to delete contacts, documents or payments. Issued documents are voided, never deleted.
Related
Something unclear or out of date? Tell us at hello@invoa.io and we'll fix the page.