Langsung ke konten
InolabInolab

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://api.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://api.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-Id tidak 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

CakupanMengizinkan
customers:readLihat pelanggan
customers:writeTambah, ubah, dan hapus pelanggan
invoices:readLihat tagihan, tagihan berulang, dan ringkasan
invoices:writeBuat, ubah, terbitkan, dan batalkan tagihan; kelola tagihan berulang
payments:writeCatat dan hapus pembayaran tagihan
payments:refundKembalikan dana pembayaran online ke pelanggan lewat akun Xendit usaha
quotes:readLihat penawaran harga
quotes:writeBuat, kirim, dan catat jawaban penawaran; jadikan tagihan
expenses:readLihat biaya
expenses:writeCatat, ubah, dan hapus biaya
projects:readLihat proyek dan ringkasannya
projects:writeBuat, ubah, dan hapus proyek
products:readLihat katalog produk dan jasa
products:writeTambah, ubah, dan hapus isi katalog
deals:readLihat peluang
deals:writeBuat, ubah, pindah tahap, dan hapus peluang
activities:readLihat aktivitas pelanggan
activities:writeCatat aktivitas dan selesaikan tindak lanjut
reports:readLihat laporan keuangan
agents:readLihat agen dan hasil kerjanya
agents:runJalankan dan batalkan agen (persetujuan hanya oleh orang, di web atau aplikasi)
usage:readLihat pemakaian AI dan biayanya
audit_log:readBaca log aktivitas

Endpoint akun, langganan, dan notifikasi hanya untuk aplikasi Inolab dan tidak dapat diakses dengan kunci API.

Endpoint

MetodePathCakupanKeterangan
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 /recurring-invoices invoices:read Jadwal tagihan berulang; ?status=active|paused|ended, ?customer_id=
GET /recurring-invoices/{id} invoices:read Satu jadwal beserta tiga tanggal berikutnya
POST /recurring-invoices invoices:write Buat jadwal: title, customer_id, interval=month|year, interval_count, next_issue_on, due_days, send_automatically, items
PUT /recurring-invoices/{id} invoices:write Ubah jadwal; tagihan yang sudah dibuat tidak berubah
POST /recurring-invoices/{id}/pause invoices:write Jeda; /resume untuk melanjutkan
DELETE /recurring-invoices/{id} invoices:write Hapus jadwal
GET /quotes quotes:read Daftar penawaran; ?status=draft|sent|expired|accepted|declined, ?customer_id=, ?deal_id=
GET /quotes/{id} quotes:read Satu penawaran beserta item
GET /quotes/{id}/pdf quotes:read PDF penawaran (application/pdf)
POST /quotes quotes:write Buat draf penawaran: customer_id, issue_date, valid_until, items
PUT /quotes/{id} quotes:write Ubah draf penawaran
POST /quotes/{id}/send quotes:write Kirim; email_customer=true untuk mengirim email dengan PDF
POST /quotes/{id}/accept quotes:write Catat diterima; /decline dengan reason untuk ditolak
POST /quotes/{id}/convert quotes:write Jadikan draf tagihan sekali; perlu juga invoices:write
DELETE /quotes/{id} quotes:write Hapus draf penawaran
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
GET /projects projects:read Daftar proyek; ?status=open|all|..., ?customer_id=, ?q=
GET /projects/{id} projects:read Satu proyek beserta ringkasan tagihan, biaya, dan margin
POST /projects projects:write Buat proyek: customer_id, name, status, contract_value
PUT /projects/{id} projects:write Ubah proyek
DELETE /projects/{id} projects:write Hapus proyek tanpa tagihan atau biaya
GET /products products:read Katalog produk dan jasa; ?type=service|goods, ?q=
GET /products/{id} products:read Satu isi katalog
POST /products products:write Tambah ke katalog: type, name, sku, unit, unit_price
PUT /products/{id} products:write Ubah isi katalog; tagihan lama tidak berubah
DELETE /products/{id} products:write Hapus dari katalog
GET /deals deals:read Daftar peluang; ?stage=open|won|..., ?customer_id=, ?owner_id=
GET /deals/{id} deals:read Satu peluang
POST /deals deals:write Buat peluang: customer_id, title, stage, value
PUT /deals/{id} deals:write Ubah peluang
POST /deals/{id}/stage deals:write Pindah tahap; lost_reason bila kalah
DELETE /deals/{id} deals:write Hapus peluang
GET /activities activities:read Daftar aktivitas; ?customer_id=, ?deal_id=, ?follow_up=due
POST /customers/{id}/activities activities:write Catat aktivitas: type, happened_on, body, follow_up_on
POST /activities/{id}/complete activities:write Tandai tindak lanjut selesai
DELETE /activities/{id} activities:write Hapus aktivitas
GET /reports/aging reports:read Umur piutang per pelanggan; ?as_of=
GET /reports/revenue reports:read Ditagihkan dan diterima per bulan; ?year=
GET /reports/cash-flow reports:read Uang masuk dan keluar per bulan; ?year=
GET /reports/profit-and-loss reports:read Laba rugi sederhana; ?year=, ?month=
GET /reports/vat reports:read PPN keluaran dan masukan per bulan; ?year=
GET /reports/quotes reports:read Penawaran dikirim dan diterima per bulan, tingkat diterima; ?year=
GET /reports/recurring-revenue reports:read Pendapatan berulang setara per bulan dan tagihan 90 hari ke depan
GET /agents agents:read Agen yang boleh dijalankan, input-nya, dan apakah AI siap
POST /agents/{agent}/runs agents:run Jalankan agen; header Idempotency-Key agar aman diulang
GET /agent-runs agents:read Daftar pekerjaan agen; ?status=, ?agent=
GET /agent-runs/{id} agents:read Satu pekerjaan agen beserta riwayat status; persetujuan memberi tautan ke web
POST /agent-runs/{id}/cancel agents:run Batalkan pekerjaan agen

Contoh menambah pelanggan:

curl -X POST "https://api.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, atau 12 persen. 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"
}
StatusArti
401Kunci tidak ada, salah, sudah dicabut, atau kedaluwarsa.
403Cakupan kunci tidak mencukupi (code: missing_scope) atau peran pembuat kunci tidak mengizinkan.
404Data tidak ada atau milik usaha lain.
422Data tidak valid; rinciannya di errors per kolom.
429Terlalu 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.

EventDikirim saat
customer.createdPelanggan ditambahkan
customer.updatedData pelanggan diubah
customer.deletedPelanggan dihapus
invoice.createdDraf tagihan dibuat
invoice.updatedDraf tagihan diubah
invoice.sentTagihan diterbitkan
invoice.paidTagihan lunas
invoice.voidedTagihan dibatalkan
invoice.deletedDraf tagihan dihapus
invoice.shippedPengiriman barang dicatat
invoice.payment_recordedPembayaran tagihan dicatat
invoice.payment_deletedCatatan pembayaran dihapus
invoice.payment_refundedSebagian pembayaran dikembalikan
quote.createdDraf penawaran dibuat
quote.updatedDraf penawaran diubah
quote.sentPenawaran dikirim
quote.acceptedPenawaran diterima pelanggan
quote.declinedPenawaran ditolak pelanggan
quote.deletedDraf penawaran dihapus
deal.createdPeluang dibuat
deal.updatedPeluang diubah
deal.stage_changedTahap peluang berubah (termasuk menang atau kalah)
deal.deletedPeluang dihapus
project.createdProyek dibuat
project.updatedProyek diubah
project.deletedProyek 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": [ { "product_id": null, "description": "Jasa desain kemasan", "quantity": 1, "unit": "paket", "unit_price": 5000000, "amount": 5000000 } ],
    "payments": [ ... ],
    "paid_at": "2026-10-09T14:30:00+07:00"
  }
}
  • data berisi objek dengan bentuk yang sama seperti respons API. Event pembayaran berisi data.payment dan data.invoice.
  • Balas dengan status 2xx dalam 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 id event (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.

OAuth untuk aplikasi pihak ketiga

Bila aplikasi Anda dipakai banyak usaha, minta izin lewat OAuth 2.0 (authorization code, PKCE S256 dianjurkan) alih-alih meminta mereka menyalin kunci API. Daftarkan aplikasi di Pengaturan › Integrasi › Aplikasi OAuth untuk mendapat client ID dan client secret.

  1. Arahkan pengguna ke https://app.inolab.id/oauth/authorize?response_type=code&client_id=…&redirect_uri=…&scope=customers:read invoices:read&state=…&code_challenge=…&code_challenge_method=S256.
  2. Pemilik atau admin memilih usaha dan menyetujui izin. Inolab kembali ke redirect_uri dengan code dan state, atau error=access_denied.
  3. Dari server Anda, tukar kode (berlaku 10 menit, sekali pakai) di POST https://app.inolab.id/oauth/token dengan grant_type=authorization_code, code, redirect_uri, code_verifier, dan kredensial klien (HTTP Basic atau client_id/client_secret di body).
  4. Respons berisi access_token (Bearer) yang berlaku 365 hari untuk satu usaha dengan izin yang disetujui, dipakai persis seperti kunci API. Usaha dapat mencabutnya kapan saja.

Yang belum tersedia

  • SDK resmi dan refresh token OAuth. Gunakan openapi.yaml untuk membuat klien.

Pertanyaan teknis: support@inolab.id.