Invoicing
Create invoices with line items, issue them, generate branded PDFs and payable links, and track installments.
Invoices let you bill a customer for itemized amounts and collect payment — in full or in installments — through the same hosted checkout.
Create an invoice
POST /invoices with one or more line items. Amounts are in
minor units.
curl -X POST http://localhost:8080/api/v1/invoices \
-H "X-Api-Key: oi_test_xxx" -H "X-Api-Secret: your-secret" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: inv-9b1c…" \
-d '{
"customerReference": "cust_123",
"customerEmail": "[email protected]",
"currency": "BDT",
"dueDate": "2026-07-01",
"issue": true,
"lineItems": [
{ "description": "Setup fee", "qty": 1, "unitAmountMinor": 50000 },
{ "description": "Monthly plan", "qty": 2, "unitAmountMinor": 25000 }
]
}'| Field | Required | Notes |
|---|---|---|
customerReference | yes | Your customer identifier. |
lineItems[] | yes | At least one; each has description, qty (> 0), unitAmountMinor (≥ 0). |
issue | no | true (default) issues immediately and assigns a number; false keeps it a draft. |
dueDate | no | ISO date. An issued invoice past due is treated as overdue. |
currency | no | Defaults to BDT. |
The total is the sum of qty × unitAmountMinor across line items. The response
includes the assigned number, status, totalMinor, paidMinor, the line
items, and any settling payments.
Draft vs issued
- Draft (
issue: false) — editable placeholder, no number, not billable. - Issued — assigned a per-app sequential
number, billable, can be paid or voided.
Collect payment
Mint a hosted-checkout link for the outstanding balance:
curl -X POST http://localhost:8080/api/v1/invoices/100/payable-link \
-H "X-Api-Key: oi_test_xxx" -H "X-Api-Secret: your-secret"{ "data": { "reference": "pay_def", "checkoutUrl": "http://localhost:3000/pay/pay_def" },
"meta": { "success": true, "timestamp": "…" } }Send the customer to checkoutUrl. The customer-facing branded invoice view is
served (unauthenticated) at GET /pay/{reference}. Each settling payment is linked
back to the invoice and moves it toward PAID.
Installments
An invoice can be settled by multiple payments. Each one that lands moves the
invoice to PARTIALLY_PAID, and the final one to PAID. paidMinor tracks the
running total; the remaining balance is totalMinor − paidMinor.
Lifecycle
stateDiagram-v2
[*] --> DRAFT
DRAFT --> ISSUED: issue
ISSUED --> PARTIALLY_PAID: installment settled
PARTIALLY_PAID --> PAID
ISSUED --> PAID: paid in full
ISSUED --> VOID: voided
ISSUED --> OVERDUE: past due, unpaid| Status | Meaning |
|---|---|
DRAFT | Not yet issued. |
ISSUED | Has a number, billable. |
PARTIALLY_PAID | Some balance settled. |
PAID | Fully settled. |
VOID | Cancelled before settlement. |
OVERDUE | Issued and past dueDate while unpaid. |
Voiding
POST /invoices/{id}/void cancels a draft or issued invoice. Voiding emits an
invoice.voided webhook.
Branding
A PDF of an invoice is available at GET /invoices/{id}/pdf. Branding (logo,
company, footer, custom CSS) is set per app via PUT /invoices/template and read
with GET /invoices/template.
Issuing, partial payment, full payment, and voiding all emit
webhooks (invoice.issued, invoice.partially_paid,
invoice.paid, invoice.voided) so your app can stay in sync without polling.