OI Payments Docs
Guides

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 }
    ]
  }'
FieldRequiredNotes
customerReferenceyesYour customer identifier.
lineItems[]yesAt least one; each has description, qty (> 0), unitAmountMinor (≥ 0).
issuenotrue (default) issues immediately and assigns a number; false keeps it a draft.
dueDatenoISO date. An issued invoice past due is treated as overdue.
currencynoDefaults 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
StatusMeaning
DRAFTNot yet issued.
ISSUEDHas a number, billable.
PARTIALLY_PAIDSome balance settled.
PAIDFully settled.
VOIDCancelled before settlement.
OVERDUEIssued 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.

On this page