API Pembuatan Invoice

Buat PDF invoice profesional secara programatis dari server Anda sendiri, cocok untuk sistem internal, marketplace, atau produk SaaS Anda.

1. Autentikasi#

Buat API key di app.invosend.com/settings/api-keys. Workspace harus memakai paket berbayar dan role Anda harus owner.

Kirim key sebagai Bearer token di header Authorization pada setiap request:

Authorization: Bearer isk_live_xxxxxxxxxxxxxxxxxxxxxxxx
API key yang kosong, salah format, tidak valid, atau dicabut akan ditolak dengan 403. Key juga ditolak jika workspace masih gratis, suspended, atau expired.

2. Endpoint#

POST/api/invoices/generate-external

Base URL: https://app.invosend.com

Deskripsi API yang bisa dibaca mesin: OpenAPI 3.1 dan Postman collection. Impor salah satunya ke Postman, Insomnia, atau generator SDK.

3. Request Body#

FieldTypeWajibDeskripsi
customer_namestringYaNama klien atau penerima invoice.
customer_emailstringTidakAlamat email valid. Invoice yang dibuat lewat panggilan ini tidak dikirim lewat email (API ini tidak mengirim email). Wajib diisi untuk invoice recurring: setiap invoice berikutnya yang dibuat otomatis dikirim ke alamat ini.
customer_tax_idstringTidakNomor pajak klien (NPWP, maksimal 40 karakter). Dicetak di bawah alamat klien pada invoice.
itemsarrayYaMinimal 1 item valid dengan deskripsi, quantity > 0, dan price >= 0.
items[].descriptionstringTidakNama produk atau layanan. Jika kosong, diisi Item.
items[].quantitynumberYaJumlah unit, maksimal 2 angka desimal (misalnya 1.5 jam), dari 0.01 sampai 999.999.
items[].pricenumberYaHarga per unit.
items[].discountnumberTidakNominal diskon per item. Default 0.
currencystringTidakKode mata uang, seperti IDR atau USD. Default IDR.
tax_ratenumberTidakPersentase pajak dari subtotal setelah diskon. Default 0.
due_datestringTidakFormat YYYY-MM-DD. Default 14 hari setelah dibuat.
notesstringTidakCatatan tambahan yang tampil di invoice.
items[].unitstringTidakLabel satuan di samping quantity, misalnya pcs atau jam.
items[].tax_ratenumberTidakOverride persentase pajak per item (0-100). Kosongkan untuk ikut tax_rate level-invoice.
discount_totalnumberTidakDiskon level-invoice. Dikurangkan sebelum pajak, jadi pajak dihitung dari jumlah setelah diskon (dibagi ke item sesuai nilainya). Default 0.
templatestringTidakID template invoice yang dipakai. Lihat daftarnya lewat GET /api/invoices/external/templates. Jika kosong, dipakai template default yang dipilih di Pengaturan, lalu default Invosend.
document_languagestringTidakBahasa kata-kata pada dokumen invoice, yaitu PDF dan email: en (bawaan), id, es, fr, de, nl, it, pt, zh, ja, hi, ar (Indonesia, Spanyol, Prancis, Jerman, Belanda, Italia, Portugis Brasil, Mandarin Sederhana, Jepang, Hindi, Arab). Nilai lain ditolak dengan pesan galat. Tata letak bawaan polos, yang hanya dipakai bila tidak ada template, selalu berbahasa Inggris.
custom_payment_notesstringTidakInstruksi pembayaran tambahan yang tampil di PDF.
footer_textstringTidakTeks singkat di bagian bawah PDF.
is_recurringbooleanTidakMembuat rangkaian invoice recurring. Invoice ini menjadi yang pertama; invoice berikutnya dibuat dan dikirim otomatis ke customer_email sesuai jadwal, masing-masing memakai 1 credit invoice. Butuh paket dengan fitur recurring invoice, serta recurring_interval dan customer_email.
recurring_intervalstringTidakSeberapa sering rangkaian berulang: salah satu dari weekly, fortnightly, monthly, quarterly, semi-annually, yearly. Wajib diisi kalau is_recurring bernilai true.
recurring_countintegerTidakJumlah total invoice dalam rangkaian, termasuk invoice pertama ini: bilangan bulat 2 sampai 121. Default 4. Pakai ini atau recurring_end_date, jangan keduanya.
recurring_end_datestringTidakTanggal setelah itu tidak ada invoice baru yang dibuat, format YYYY-MM-DD, harus di masa depan. Pakai ini atau recurring_count, jangan keduanya.
payment_gatewaystringTidakSalah satu dari midtrans, xendit, stripe, paypal, razorpay, mollie. Gateway harus sudah terhubung di workspace.
payment_accountstringTidakID rekening pembayaran (detail bank) yang sudah terdaftar, untuk dilampirkan ke invoice.

Contoh request lengkap:

curl -X POST https://app.invosend.com/api/invoices/generate-external \
  -H "Authorization: Bearer isk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_name": "Acme Corp",
    "customer_email": "billing@acme.com",
    "items": [
      { "description": "Consulting Services", "quantity": 2, "price": 500000, "tax_rate": 11 }
    ],
    "discount_total": 50000,
    "currency": "IDR",
    "notes": "Thank you for your business."
  }'

Contoh invoice recurring (invoice pertama sekarang, lalu satu setiap bulan, total 6):

curl -X POST https://app.invosend.com/api/invoices/generate-external \
  -H "Authorization: Bearer isk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_name": "Acme Corp",
    "customer_email": "billing@acme.com",
    "items": [
      { "description": "Monthly retainer", "quantity": 1, "price": 2500000 }
    ],
    "currency": "IDR",
    "recurring_interval": "monthly",
    "recurring_count": 6
  }'
Agar aman diulang setelah timeout atau gangguan jaringan, tambahkan header Idempotency-Key (1-255 karakter, misalnya UUID yang Anda buat untuk setiap invoice). Jika request pertama berhasil, pengulangan mengembalikan respons yang sama dengan header Idempotent-Replayed: true, tanpa membuat invoice lagi atau memakai kredit lagi (credits_remaining adalah nilai dari panggilan pertama). Key disimpan 24 jam per workspace. Hanya respons sukses yang disimpan, jadi request yang gagal bisa diulang dengan key yang sama.

4. Response#

200 OK berarti invoice berhasil dibuat dan PDF sudah digenerate:

{
  "data": {
    "id": "cjld2cjxh0000qzrmn831i7rn",
    "invoice_number": "INV-1-202609-0016",
    "pdf_url": "https://cdn.invosend.com/company/1/pdf/Invoice_INV_1_202609_0016_UNPAID_0da2beeabb.pdf",
    "credits_remaining": 24,
    "is_recurring": false,
    "template": {
      "id": "k3p9x2m7c0000abcd1234efgh",
      "name": "Classic",
      "source": "system_default"
    }
  }
}
credits_remaining bernilai null jika workspace memakai kuota unlimited. id dipakai untuk memanggil Endpoint Tambahan di bawah. Invoice dari API juga muncul di daftar invoice workspace dan memakai urutan nomor yang sama dengan invoice manual. Nomor invoice berbentuk INV-{id workspace}-{YYYYMM}-{urutan}. Untuk invoice recurring, respons juga berisi objek recurring: schedule_id, interval, occurrences, next_issue_date, end_date. recurring.schedule_id adalah id yang dipakai pada endpoint recurring di bawah.

5. Kode Error#

StatusPenyebab
400Field wajib kosong atau tidak valid: customer_name, items kosong, tidak ada item valid, payment_gateway tidak dikenal, payment_gateway yang belum terhubung untuk workspace ini, atau, untuk invoice recurring, recurring_interval atau customer_email kosong, serta recurring_count / recurring_end_date tidak valid. Status ini juga muncul jika PDF gagal dibuat: ulangi request (tidak ada credit yang terpotong).
402Saldo invoice_credits workspace habis. Top up kredit untuk melanjutkan.
403Header Authorization kosong atau salah format, API key tidak valid/dicabut, atau paket workspace tidak menyertakan akses API.
403 (recurring)is_recurring diisi tapi paket workspace tidak menyertakan fitur recurring invoice.
409Request dengan Idempotency-Key yang sama masih diproses. Coba lagi sebentar lagi.
422Idempotency-Key sudah dipakai dengan isi request yang berbeda. Gunakan key baru untuk invoice baru.
429Rate limit terlampaui. Lihat bagian Rate Limit.

6. Rate Limit#

ScopeLimit
Per workspace (semua request API digabung)Professional 10/min · Specialist 20/min · Business 30/min · Enterprise 60/min
Per alamat IP (semua request API digabung)30 request/menit
API key aktif per workspaceProfessional 3 · Specialist 5 · Business 10 · Enterprise 20
Pembuatan invoice dan endpoint baca/kelola memakai batas yang sama. Respons sukses menyertakan header X-RateLimit-Limit dan X-RateLimit-Remaining. Kuota API workspace dikonfigurasi berdasarkan paket membership Invosend. Butuh volume produksi lebih besar? Hubungi kami dari akun workspace Anda. Key yang sudah dicabut tidak dihitung dalam batas jumlah key.

7. Endpoint Tambahan#

API key yang sama juga bisa dipakai membaca dan mengelola invoice yang dibuat lewat generate-external:

MethodPathDeskripsi
GET/api/invoices/externalList invoice milik workspace ini. Mendukung query ?status= dan ?page=/?pageSize=.
GET/api/invoices/external/templatesDaftar template invoice yang bisa diisi di template, beserta apakah paket Anda mengizinkannya dan mana yang menjadi default.
GET/api/invoices/external/:idAmbil satu invoice berdasarkan id (nilai yang dikembalikan generate-external).
PUT/api/invoices/external/:id/statusUbah status invoice. Body: { "status": "paid" } (salah satu dari unpaid, paid, overdue, cancelled).
PUT/api/invoices/external/:id/archiveArsipkan atau batalkan arsip invoice. Body: { "is_archived": true }.
POST/api/invoices/external/:id/pdfAmbil ulang URL PDF, regenerate kalau perlu. Body: { "force": true } untuk memaksa regenerasi.
GET/api/invoices/external/recurringDaftar rangkaian recurring. Mendukung ?status= (active, paused, completed) dan ?page=/?pageSize=.
GET/api/invoices/external/recurring/:idAmbil satu rangkaian (recurring.schedule_id dari generate-external) beserta semua kemunculannya: tanggal, status, dan id invoice yang dihasilkan masing-masing.
PUT/api/invoices/external/recurring/:id/statusJeda atau lanjutkan rangkaian. Body: { "status": "paused" } atau { "status": "active" }. Melanjutkan membutuhkan paket dengan invoice recurring.
PUT/api/invoices/external/recurring/:id/extendTambah kemunculan setelah yang terakhir. Body: { "count": 3 } atau { "end_date": "2027-12-31" }.
PUT/api/invoices/external/recurring/:id/frequencyUbah seberapa sering rangkaian berulang; kemunculan mendatang yang belum dibuat akan diganti. Body: { "frequency": "monthly", "start_date": "2027-01-01", "count": 6 } (boleh memakai end_date sebagai ganti count; start_date harus di masa depan).
POST/api/invoices/external/recurring/:id/skipHapus satu kemunculan mendatang. Body: { "date": "2027-01-01" }. Kemunculan yang sudah menghasilkan invoice tidak bisa dihapus.
Untuk menerima pembayaran online, isi payment_gateway (dan opsional payment_account) pada generate-external, lalu kirim ke pelanggan halaman pembayaran publik invoice: https://app.invosend.com/p/invoice/{id}, dengan id yang dikembalikan API. Pelanggan membayar di sana lewat gateway yang terhubung dan invoice otomatis ditandai lunas. Checkout ditangani halaman itu; tidak ada endpoint checkout publik terpisah yang perlu dipanggil.

8. Webhook#

Tanpa perlu polling, daftarkan URL HTTPS dan Invosend akan mengirim request POST berisi JSON setiap kali ada perubahan pada invoice. Tambahkan endpoint di Pengaturan > Webhooks (pemilik workspace, paket dengan akses API, maksimal 5 endpoint). Setiap endpoint punya secret penandatanganan sendiri yang hanya ditampilkan sekali.

Event dikirim untuk invoice yang dibuat di dashboard maupun lewat API ini. Template invoice berulang tidak pernah dikirim.

EventDikirim saat
invoice.createdInvoice dibuat. PDF bisa jadi belum siap, sehingga pdf_url dapat bernilai null.
invoice.paidStatus berubah menjadi paid (oleh Anda, lewat API, atau lewat pembayaran online).
invoice.overdueStatus berubah menjadi overdue (otomatis setelah jatuh tempo, atau manual).
invoice.cancelledStatus berubah menjadi cancelled.

Setiap event memakai amplop yang sama. data.object adalah invoice dengan bentuk yang sama seperti GET /api/invoices/external/:id; event perubahan status juga menyertakan data.previous_status:

{
  "id": "evt_9f2c1b7a4e3d5c6b8a1f0e2d",
  "object": "event",
  "type": "invoice.paid",
  "created": "2026-10-01T09:30:12.000Z",
  "data": {
    "object": {
      "id": "cjld2cjxh0000qzrmn831i7rn",
      "invoice_number": "INV-1-202610-0007",
      "customer_name": "Acme Corp",
      "customer_email": "billing@acme.com",
      "currency": "USD",
      "subtotal": 500,
      "discount_total": 0,
      "tax_rate": 0,
      "tax_amount": 0,
      "amount": 500,
      "amount_paid": 500,
      "amount_credited": 0,
      "amount_received": 500,
      "invoice_status": "paid",
      "is_archived": false,
      "is_recurring": false,
      "recurring_interval": "none",
      "issue_date": "2026-10-01T02:15:40.000Z",
      "due_date": "2026-10-15",
      "paid_at": "2026-10-01T09:30:11.000Z",
      "pdf_url": "https://cdn.invosend.com/company/1/pdf/Invoice_INV_1_202610_0007_PAID.pdf",
      "pdf_status": "ready"
    },
    "previous_status": "unpaid"
  }
}

Setiap request membawa header berikut. Untuk memverifikasinya, hitung HMAC-SHA256 dari {t}.{body mentah} dengan secret endpoint lalu bandingkan dengan v1; tolak bila t lebih lama dari 5 menit:

POST /your/webhook/url
Content-Type: application/json
Invosend-Event: invoice.paid
Invosend-Delivery: evt_9f2c1b7a4e3d5c6b8a1f0e2d
Invosend-Signature: t=1790847012,v1=5b1d...e9a2

// Node.js
import crypto from "crypto";

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(parts.t + "." + rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
Balas dengan status 2xx apa pun dalam 10 detik. Selain itu akan dicoba lagi hingga 7 kali (setelah 1 menit, 5 menit, 30 menit, 2 jam, 6 jam, 12 jam, dan 24 jam). Endpoint yang terus gagal akan dimatikan dan Anda diberi notifikasi. Event bisa datang lebih dari sekali atau tidak berurutan: gunakan id event untuk mengabaikan duplikat dan anggap invoice_status sebagai sumber kebenaran. Log pengiriman disimpan 30 hari, dan event mana pun bisa dikirim ulang dari Pengaturan > Webhooks.

9. Batasan#

API ini sengaja dibatasi untuk pembuatan invoice dan pengelolaan ringan. Berikut yang belum tersedia:

  • Webhook hanya mencakup invoice dibuat, dibayar, jatuh tempo, dan dibatalkan. Perubahan lain, seperti edit atau pengarsipan, tidak dikirim: baca kondisi terbaru dengan GET /invoices/external/:id.
  • Invoice yang dibuat tiap request tidak dikirim lewat email: API ini hanya men-generate PDF, jadi mengirimkannya ke pelanggan jadi tanggung jawab Anda (atau pakai dashboard). Pengecualian: rangkaian recurring, di mana setiap invoice lanjutan yang dibuat penjadwal dikirim otomatis ke customer_email.
  • Invoice tidak bisa diedit setelah dibuat — hanya status dan status arsipnya yang bisa diubah lewat API ini.
  • Payment gateway harus sudah terhubung di dashboard lebih dulu; API ini hanya melampirkannya ke invoice, tidak menghubungkan gateway itu sendiri.
  • Rangkaian recurring bisa dijeda, dilanjutkan, diperpanjang, diubah jadwalnya, dan kemunculan mendatangnya dilewati lewat API, tetapi isinya (item, pelanggan, nominal) tidak bisa diedit dan rangkaian tidak bisa dihapus lewat API; lakukan itu di dashboard.

Pertanyaan Umum#

Bagaimana cara mendapatkan API key Invosend?

Buat akun di app.invosend.com, upgrade workspace ke paket berbayar, lalu buka Settings > API Keys dan klik Generate key. Key asli hanya ditampilkan sekali saat dibuat.

Berapa biaya setiap panggilan API generate invoice?

Setiap request berhasil akan mengurangi 1 kredit invoice dari saldo workspace Anda. Kreditnya sama dengan kredit invoice yang dibuat lewat dashboard.

Berapa rate limit API ini?

Rate limit workspace mengikuti konfigurasi paket berbayar Anda. Bagian Rate Limit di atas memuat batas terbaru untuk setiap paket.