API dan webhook
API Inolab dipakai untuk menghubungkan pelanggan, tagihan, dan pembayaran di Inolab dengan sistem lain, misalnya toko online, aplikasi kasir, atau software akuntansi. Semua permintaan dan respons berformat JSON melalui HTTPS.
Base URL: https://app.inolab.id/api/v1 · Spesifikasi OpenAPI 3.1: openapi.yaml
Autentikasi
Buat kunci API di aplikasi, di Pengaturan › Integrasi. Hanya pemilik dan admin usaha yang dapat membuatnya. Kunci hanya ditampilkan sekali; simpan di tempat yang aman dan jangan taruh di kode yang berjalan di peramban atau aplikasi seluler.
Kirim kunci di header Authorization setiap permintaan:
curl "https://app.inolab.id/api/v1/invoices?status=overdue" \
-H "Authorization: Bearer 12|inolab_xxxxxxxxxxxxxxxxxxxx" \
-H "Accept: application/json"
- Kunci berlaku untuk satu usaha, yaitu usaha tempat kunci dibuat. Header
X-Tenant-Idtidak diperlukan. - Kunci bertindak atas nama pembuatnya. Izinnya dibatasi cakupan yang dipilih dan tidak pernah melebihi peran pembuatnya. Bila pembuatnya keluar dari usaha, kunci berhenti bekerja.
- Kunci dapat dicabut kapan saja dari halaman yang sama.
Izin kunci API
| Cakupan | Mengizinkan |
|---|---|
customers:read | Lihat pelanggan |
customers:write | Tambah, ubah, dan hapus pelanggan |
invoices:read | Lihat tagihan dan ringkasan |
invoices:write | Buat, ubah, terbitkan, dan batalkan tagihan |
payments:write | Catat dan hapus pembayaran tagihan |
expenses:read | Lihat biaya |
expenses:write | Catat, ubah, dan hapus biaya |
Endpoint akun, langganan, dan notifikasi hanya untuk aplikasi Inolab dan tidak dapat diakses dengan kunci API.
Endpoint
| Metode | Path | Cakupan | Keterangan |
|---|---|---|---|
GET |
/customers |
customers:read |
Daftar pelanggan; ?q=, ?type=company|individual, ?per_page= (maks. 100) |
GET |
/customers/{id} |
customers:read |
Satu pelanggan |
POST |
/customers |
customers:write |
Tambah pelanggan |
PUT |
/customers/{id} |
customers:write |
Ubah pelanggan |
DELETE |
/customers/{id} |
customers:write |
Hapus pelanggan tanpa tagihan terbuka |
GET |
/invoices |
invoices:read |
Daftar tagihan; ?status=draft|open|overdue|paid|void, ?q=, ?customer_id= |
GET |
/invoices/{id} |
invoices:read |
Satu tagihan beserta item dan pembayaran |
GET |
/invoices/{id}/pdf |
invoices:read |
PDF tagihan (application/pdf) |
GET |
/dashboard |
invoices:read |
Angka piutang dan tagihan lewat jatuh tempo |
POST |
/invoices |
invoices:write |
Buat draf tagihan; total dihitung server |
PUT |
/invoices/{id} |
invoices:write |
Ubah draf |
DELETE |
/invoices/{id} |
invoices:write |
Hapus draf |
POST |
/invoices/{id}/send |
invoices:write |
Terbitkan; email_customer=true untuk mengirim email |
POST |
/invoices/{id}/void |
invoices:write |
Batalkan tagihan tanpa pembayaran; reason opsional |
POST |
/invoices/{id}/payments |
payments:write |
Catat pembayaran: amount, paid_on, method, reference, notes |
DELETE |
/invoices/{id}/payments/{paymentId} |
payments:write |
Hapus catatan pembayaran |
GET |
/expenses |
expenses:read |
Daftar biaya; ?from=, ?to=, ?category=, ?q= |
GET |
/expenses/{id} |
expenses:read |
Satu biaya |
POST |
/expenses |
expenses:write |
Catat biaya: spent_on, category, description, amount, tax_amount, method |
PUT |
/expenses/{id} |
expenses:write |
Ubah biaya |
DELETE |
/expenses/{id} |
expenses:write |
Hapus biaya |
Contoh menambah pelanggan:
curl -X POST "https://app.inolab.id/api/v1/customers" \
-H "Authorization: Bearer 12|inolab_xxxxxxxxxxxxxxxxxxxx" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"type": "company", "name": "PT Maju Bersama", "email": "keuangan@majubersama.co.id"}'
- Nilai uang dalam Rupiah utuh tanpa desimal, misalnya
5550000. - Tanggal berformat
YYYY-MM-DD; waktu berformat ISO 8601 dengan zona waktu. - Tarif pajak tagihan:
0,11, atau12persen. Nomor, subtotal, pajak, dan total dihitung server. - Metode pembayaran:
bank_transfer,qris,ewallet,cash,card,other.
Respons dan galat
Setiap respons memakai amplop yang sama. Daftar berhalaman menyertakan meta.pagination.
{
"success": true,
"message": "Permintaan berhasil.",
"data": [ ... ],
"meta": { "pagination": { "current_page": 1, "per_page": 25, "total": 42, "last_page": 2 } }
}
{
"success": false,
"message": "Kunci API ini tidak memiliki izin untuk tindakan ini.",
"code": "missing_scope"
}
| Status | Arti |
|---|---|
401 | Kunci tidak ada, salah, sudah dicabut, atau kedaluwarsa. |
403 | Cakupan kunci tidak mencukupi (code: missing_scope) atau peran pembuat kunci tidak mengizinkan. |
404 | Data tidak ada atau milik usaha lain. |
422 | Data tidak valid; rinciannya di errors per kolom. |
429 | Terlalu banyak permintaan. Batasnya 120 permintaan per menit per pengguna; tunggu sesuai header Retry-After. |
Kirim Accept-Language: en untuk pesan dalam bahasa Inggris. Bawaannya bahasa Indonesia.
Webhook
Webhook memberi tahu sistem Anda saat data berubah, sehingga Anda tidak perlu memeriksa API berulang kali. Tambahkan alamat penerima di Pengaturan › Integrasi dan pilih event yang ingin diterima. Alamat harus HTTPS publik di port 443; alamat jaringan internal ditolak.
| Event | Dikirim saat |
|---|---|
customer.created | Pelanggan ditambahkan |
customer.updated | Data pelanggan diubah |
customer.deleted | Pelanggan dihapus |
invoice.created | Draf tagihan dibuat |
invoice.updated | Draf tagihan diubah |
invoice.sent | Tagihan diterbitkan |
invoice.paid | Tagihan lunas |
invoice.voided | Tagihan dibatalkan |
invoice.deleted | Draf tagihan dihapus |
invoice.payment_recorded | Pembayaran tagihan dicatat |
invoice.payment_deleted | Catatan pembayaran dihapus |
Setiap event dikirim sebagai POST berisi JSON:
POST /webhooks/inolab HTTP/1.1
Content-Type: application/json
User-Agent: Inolab-Webhooks/1.0
Inolab-Event: invoice.paid
Inolab-Event-Id: evt_01k7c3m0q4f9w2v8t6r5y1x3za
Inolab-Delivery: 01k7c3m0r8a2b4c6d8e0f2g4h6
Inolab-Signature: t=1791520200,v1=5f2b9c…
{
"id": "evt_01k7c3m0q4f9w2v8t6r5y1x3za",
"type": "invoice.paid",
"created_at": "2026-10-09T14:30:00+07:00",
"tenant_id": "01k6xq5a9d3m7p2r4t6v8x0z2b",
"data": {
"id": "01k7a0b2c4d6e8f0g2h4j6k8m0",
"number": "INV/2026/0042",
"status": "paid",
"customer": { "id": "01k6…", "name": "PT Maju Bersama", "email": "keuangan@majubersama.co.id" },
"issue_date": "2026-10-01",
"due_date": "2026-10-15",
"currency": "IDR",
"subtotal": 5000000,
"tax_rate": 11,
"tax_amount": 550000,
"total": 5550000,
"amount_paid": 5550000,
"balance_due": 0,
"items": [ { "description": "Jasa desain kemasan", "quantity": 1, "unit_price": 5000000, "amount": 5000000 } ],
"payments": [ ... ],
"paid_at": "2026-10-09T14:30:00+07:00"
}
}
databerisi objek dengan bentuk yang sama seperti respons API. Event pembayaran berisidata.paymentdandata.invoice.- Balas dengan status
2xxdalam 10 detik. Status lain, timeout, atau pengalihan (redirect) dianggap gagal. - Kiriman yang gagal dicoba ulang hingga 5 kali dalam sekitar 9 jam, lalu ditandai gagal. Riwayatnya dapat dilihat dan dikirim ulang dari aplikasi.
- Satu event bisa diterima lebih dari sekali dan urutannya tidak dijamin. Pakai
idevent (Inolab-Event-Id) untuk mengabaikan duplikat. - Tombol Kirim uji coba mengirim event
ping.
Memeriksa tanda tangan
Header Inolab-Signature berisi waktu kirim (t, detik Unix) dan v1, yaitu HMAC-SHA256 dari t, titik, lalu body mentah, dengan rahasia penandatangan webhook sebagai kunci. Tolak permintaan yang tanda tangannya tidak cocok atau waktunya lebih dari 5 menit dari sekarang.
// $secret: the signing secret shown when the webhook was created.
$body = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_INOLAB_SIGNATURE'] ?? ''), $signature);
$expected = hash_hmac('sha256', ($signature['t'] ?? '').'.'.$body, $secret);
$fresh = abs(time() - (int) ($signature['t'] ?? 0)) <= 300;
if (! $fresh || ! hash_equals($expected, $signature['v1'] ?? '')) {
http_response_code(400);
exit;
}
$event = json_decode($body, true);
// Use $event['id'] to ignore an event you have already handled.
http_response_code(200);
import crypto from 'node:crypto';
// rawBody: the request body exactly as received (a string, not parsed JSON).
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map((part) => part.split('=')));
const expected = crypto.createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) <= 300;
return fresh
&& typeof parts.v1 === 'string'
&& parts.v1.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}
Rahasia penandatangan ditampilkan sekali saat webhook dibuat. Bila bocor, buat rahasia baru dari halaman webhook; rahasia lama langsung tidak berlaku.
Yang belum tersedia
- OAuth untuk aplikasi pihak ketiga dan SDK resmi. Untuk saat ini, gunakan kunci API per usaha.
- Endpoint laporan (umur piutang, pendapatan); datanya dapat diunduh sebagai CSV dari aplikasi.
Pertanyaan teknis: support@inolab.id.