Invoices are the core resource in Rebill. Each invoice belongs to a client, contains one or more line items, and progresses through a status lifecycle from draft to paid.
Invoice statuses: draft · scheduled · sent · paid · paid_partial · cancelled
List invoices
GET /invoice
Returns invoices for your account. By default, only active invoices (draft, sent, paid_partial) are included.
| Query parameter | Type | Description |
|---|---|---|
| include_paid | boolean | Include paid invoices (default: false) |
| include_cancelled | boolean | Include cancelled invoices (default: false) |
curl "https://api.rebill.co.za/invoice?include_paid=true" \ -H "Authorization: Bearer sk_your_secret_key"
Response:
{
"invoices": [
{
"id": "abc123",
"number": "INV0001",
"status": "sent",
"client_id": "xyz789",
"currency": "ZAR",
"invoice_date": "2026-03-01T00:00:00Z",
"due_date": "2026-03-15T00:00:00Z",
"amount_due": 150000,
"items": [...]
}
]
}Get an invoice
GET /invoice/:id
curl https://api.rebill.co.za/invoice/abc123 \ -H "Authorization: Bearer sk_your_secret_key"
Find an invoice by external reference
GET /invoice/by_external_ref?ref=
Looks up an invoice by the external_ref you supplied when creating it. Useful for checking whether an invoice already exists for a source record in another system (e.g. a Booklink booking) before creating a duplicate. Returns 404 if no invoice carries that reference.
curl "https://api.rebill.co.za/invoice/by_external_ref?ref=booklink:booking:abc123" \ -H "Authorization: Bearer sk_your_secret_key"
Invoice object
| Field | Type | Description |
|---|---|---|
| id | string | Unique identifier |
| public_id | string | Short ID used in public-facing payment links |
| created | timestamp | When the invoice was created |
| number | string | Human-readable invoice number (e.g. INV0001). Empty on draft invoices - see the numbering note below |
| currency | string | ISO 4217 currency code |
| vat_enabled | boolean | Whether VAT is shown on the invoice |
| status | string | draft · scheduled · sent · paid · paid_partial · cancelled |
| client_id | string | ID of the client this invoice belongs to |
| owner_user_id | string | ID of the team member this invoice is attributed to |
| invoice_date | timestamp | Invoice issue date |
| due_date | timestamp | Payment due date |
| items | array | Line items (see Line item object) |
| amount | integer | Total invoice amount in cents |
| amount_due | integer | Outstanding balance in cents |
| notes | string | Notes printed on the invoice |
| bank_details | string | Bank account details for EFT payment |
| deposit_type | string | fixed or percentage (if applicable) |
| deposit_value | integer | Cents (fixed) or basis points (percentage) |
| deposit_amount | integer | Calculated deposit amount in cents |
| discount_type | string | fixed or percentage; invoice-level discount (if applicable) |
| discount_value | integer | Cents (fixed) or basis points (percentage) |
| discount_amount | integer | Calculated invoice-level discount in cents |
| custom_fields | object | Key-value pairs of custom fields |
| late_fee_disabled | boolean | Whether late fees are disabled for this invoice |
| late_fee_last_applied | timestamp | When a late fee was last applied (if any) |
| scheduled_send_at | timestamp | When the invoice is scheduled to be sent (only present on scheduled invoices) |
| payment_terms | string | Payment terms text printed on the invoice (e.g. "Net 30") |
| external_ref | string | Caller-supplied reference for cross-system deduplication |
| po_number | string | Client's purchase order number |
| labels | array | Internal organisation labels (strings); never shown to the client |
| batch_id | string | The invoice batch (CSV billing run) that created this invoice, when it came from one |
Line item object
| Field | Type | Description |
|---|---|---|
| type | string | product · service · item |
| name | string | Line item name |
| description | string | Description printed on the invoice |
| quantity | number | Number of units |
| unit_price | integer | Price per unit in cents |
| vat_type | string | none · standard · zero_rated · exempt |
| vat_rate | integer | VAT rate in basis points (1500 = 15%) |
| discount_type | string | fixed or percentage; per-line discount (optional) |
| discount_value | integer | Cents (fixed) or basis points (percentage) |
| service_date | date | Date the line item was performed or delivered (optional) |
| item_id | string | ID of the catalog item this line was created from, if any |
type: "late_fee" also appears on invoices, but only on system-generated late fee lines: they are created by Rebill when an overdue invoice accrues a late fee, and the app never lets you set this type yourself. Use POST /invoice/:id/waive_late_fee to remove a late fee line instead.
Create an invoice
POST /invoice
| Field | Required | Type | Description |
|---|---|---|---|
| client_id | Yes | string | ID of an existing client |
| invoice_date | Yes | date | Invoice issue date |
| due_date | Yes | date | Payment due date |
| items | Yes | array | At least one line item |
| currency | No | string | ISO 4217 code; defaults to the client, then account, default currency |
| notes | No | string | Notes to print on the invoice |
| bank_details | No | string | Bank account details for EFT |
| deposit_type | No | string | fixed or percentage |
| deposit_value | No | integer | Cents (fixed) or basis points (percentage) |
| discount_type | No | string | fixed or percentage; invoice-level discount |
| discount_value | No | integer | Cents (fixed) or basis points (percentage) |
| custom_fields | No | object | Additional key-value fields to display |
| late_fee_disabled | No | boolean | Disable late fees for this invoice (default: false) |
| payment_terms | No | string | Payment terms text, max 100 characters; defaults to your account setting |
| external_ref | No | string | Caller-supplied reference for idempotency, max 200 characters. Re-creating with the same value returns the existing invoice instead of a duplicate |
| po_number | No | string | Client's purchase order number, max 100 characters |
| owner_user_id | No | string | ID of the team member to attribute this invoice to; defaults to the caller |
| labels | No | array | Internal organisation labels, up to 10 per invoice, 40 characters each |
| scheduled_send_at | No | timestamp | ISO 8601 UTC datetime to schedule automatic email delivery (e.g. 2026-04-01T07:00:00Z). Creates the invoice with status scheduled instead of draft. |
curl -X POST https://api.rebill.co.za/invoice \
-H "Authorization: Bearer sk_your_secret_key" \
-H "Content-Type: application/json" \
-d '{
"client_id": "xyz789",
"invoice_date": "2026-03-15",
"due_date": "2026-03-30",
"currency": "ZAR",
"items": [
{
"type": "service",
"name": "Web design",
"description": "Homepage redesign",
"quantity": 1,
"unit_price": 500000,
"vat_type": "standard",
"vat_rate": 1500
}
],
"notes": "Payment due within 30 days."
}'Response (201 Created):
{
"id": "abc123"
}If scheduled_send_at is provided, the invoice is created with status scheduled and will be emailed automatically at the specified time. Otherwise it is created as draft. Fetch it by ID to get the full object.
Draft invoices carry no number
A new invoice - draft or scheduled - is created with an emptynumber field. Rebill uses gapless numbering: the next number in your sequence is only assigned the first time the invoice leaves draft, whether that's sending it, marking it as sent, capturing a payment against it, or a client opening its public payment link. This keeps abandoned drafts from consuming a number and leaving a gap in your sequence.Update an invoice
PUT /invoice/:id
Updates an existing invoice. Accepts the same fields as create (plus number to manually renumber it). Only draft invoices can have their core fields (items, dates, amounts) edited; use the dedicated endpoints below to update labels, capture payments, or waive late fees on invoices in other statuses.
Delete an invoice
DELETE /invoice/:id
Permanently deletes an invoice. Only draft invoices can be deleted.
Update invoice labels
PUT /invoice/:id/labels
Replaces the invoice's internal label list. Labels are operator-only metadata: this works regardless of invoice status, touches nothing else on the invoice, and never appears on customer-facing surfaces.
| Field | Required | Type | Description |
|---|---|---|---|
| labels | Yes | array | Full replacement list of label strings, up to 10, 40 characters each |
curl -X PUT https://api.rebill.co.za/invoice/abc123/labels \
-H "Authorization: Bearer sk_your_secret_key" \
-H "Content-Type: application/json" \
-d '{
"labels": ["vip", "needs-follow-up"]
}'Mark an invoice as sent
POST /invoice/:id/mark_as_sent
Marks a draft invoice as sent without sending any email or WhatsApp message. Useful when you have delivered the invoice through another channel and just want to update its status.
curl -X POST https://api.rebill.co.za/invoice/abc123/mark_as_sent \ -H "Authorization: Bearer sk_your_secret_key"
Send an invoice
POST /invoice/:id/send
Sends the invoice to the client and marks it as sent. Works on draft and scheduled invoices. By default sends via email. Add ?whatsapp=true to send via WhatsApp instead (requires a WhatsApp-enabled account and sufficient credits).
# Send via email curl -X POST https://api.rebill.co.za/invoice/abc123/send \ -H "Authorization: Bearer sk_your_secret_key" # Send via WhatsApp curl -X POST "https://api.rebill.co.za/invoice/abc123/send?whatsapp=true" \ -H "Authorization: Bearer sk_your_secret_key"
Cancel a scheduled invoice
POST /invoice/:id/cancel_schedule
Cancels the scheduled delivery of a scheduled invoice and reverts it to draft status. The invoice is not sent and can be edited, rescheduled, or sent manually.
curl -X POST https://api.rebill.co.za/invoice/abc123/cancel_schedule \ -H "Authorization: Bearer sk_your_secret_key"
Response (200 OK):
{
"id": "abc123"
}Invoice batches (CSV billing runs)
A batch turns a set of billing rows (one per invoice line, typically exported from another system) into draft invoices, one per client, and sends them in one background run. Rows are validated all-or-nothing, and the same rows submitted twice return the existing batch instead of a duplicate.
POST /invoice-batch/preview
Validates and groups the rows without creating anything. Takes the same rows array as create and returns the invoices the batch would create, every row error, total_by_currency, and existing_batch_id when a batch already exists for these rows.
POST /invoice-batch
| Field | Required | Type | Description |
|---|---|---|---|
| reference | No | string | Your name for the run, e.g. "September 2026" (max 100 characters) |
| rows | Yes | array | Up to 2000 row objects; every cell is a string as it appeared in the file |
| rows[].client_code | One of | string | Client code (matched first, case-insensitive) |
| rows[].client_email | One of | string | Client email, when no code is given; must match exactly one client |
| rows[].description | Yes | string | Line text, max 200 characters |
| rows[].quantity | No | string | Defaults to 1 |
| rows[].unit_price | Yes | string | Major units, e.g. "150.00"; currency signs and thousands separators are ignored |
| rows[].vat_rate | No | string | Percent. Blank = the account rate when VAT registered, otherwise no VAT; "0" = no VAT |
| rows[].invoice_date | No | string | YYYY-MM-DD; blank = today in the account timezone |
| rows[].due_date | No | string | YYYY-MM-DD; blank = invoice date + 30 days |
| rows[].po_number | No | string | Max 100 characters |
| rows[].item_type | No | string | item (default), product or service |
Rows for the same client become one invoice and must agree on invoice date, due date and PO number. Drafts take the client's default currency (else the account's), the account's VAT flag and payment terms, exactly like POST /invoice, and carry batch_id.
curl -X POST https://api.rebill.co.za/invoice-batch \
-H "Authorization: Bearer sk_your_secret_key" \
-H "Content-Type: application/json" \
-d '{
"reference": "September 2026",
"rows": [
{ "client_code": "ACME01", "description": "Hosting", "quantity": "1", "unit_price": "150.00" },
{ "client_code": "ACME01", "description": "Support hours", "quantity": "2", "unit_price": "450.00" },
{ "client_email": "[email protected]", "description": "Retainer", "unit_price": "3000", "vat_rate": "0" }
]
}'Response (201 Created):
{
"batch_id": "bat_abc123",
"invoice_count": 2
}400 with { "message": "Validation failed", "errors": [{ "row": 3, "field": "client_email", "errors": ["..."] }] } when any row fails; 409 with { "batch_id": "..." } when these rows already ran; 402 when a free account would exceed its monthly invoice limit with this batch.
GET /invoice-batch
Lists the account's batches, newest first. Each carries status (draft, sending, sent), invoice_count, sent_count, failed_count and failures (invoice_id, client_name, reason).
GET /invoice-batch/:id
Returns batch plus invoices: id, number, client_id, client_name, client_code, status, invoice_date, due_date, amount, currency and, when the last run could not deliver it, failure.
POST /invoice-batch/:id/send
Starts the send run in the background and returns 202 with pending, the number of drafts it will deliver. Each draft is numbered, marked sent and emailed to its client with the online invoice link (no PDF attachment, no WhatsApp). Poll GET /invoice-batch/:id for progress. Calling send again on a finished batch retries only the invoices still in draft; 409 while a run is in progress or when nothing is left to send.
DELETE /invoice-batch/:id
Deletes a batch that has not been sent, together with its draft invoices. 409 once anything in it has gone out.
Mark an invoice as paid
POST /invoice/:id/mark_as_paid
Records a manual payment against the invoice and marks it as paid. A payment receipt email is sent to the client automatically.
| Field | Required | Type | Description |
|---|---|---|---|
| received | Yes | date | Date the payment was received (YYYY-MM-DD) |
| method | Yes | string | Payment method (e.g. eft, cash, card, cheque) |
| reference | No | string | Payment reference or transaction ID |
curl -X POST https://api.rebill.co.za/invoice/abc123/mark_as_paid \
-H "Authorization: Bearer sk_your_secret_key" \
-H "Content-Type: application/json" \
-d '{
"received": "2026-03-20",
"method": "eft",
"reference": "REF123456"
}'Partially paid invoices
Invoices with an existing online payment record (statuspaid_partial) cannot use this endpoint. Use POST /invoice/:id/capture_payment instead to record additional payments against them.Capture a payment
POST /invoice/:id/capture_payment
Records a payment against the client's payment ledger and allocates it to this invoice (and optionally others). Any amount left unallocated becomes derived client credit. A draft or scheduled invoice is promoted to sent first. Sends a payment-received email.
| Field | Required | Type | Description |
|---|---|---|---|
| received | Yes | date | Date the payment was received (YYYY-MM-DD) |
| method | Yes | string | Payment method (e.g. eft, cash, card, cheque) |
| amount | Yes | integer | Total amount received, in cents |
| reference | No | string | Payment reference or transaction ID |
| allocations | No | array | Explicit split across invoices: [{ "invoice_id", "amount" }, ...]. Omit to allocate the full amount to this invoice (capped at its outstanding balance), with any remainder left as unallocated credit |
curl -X POST https://api.rebill.co.za/invoice/abc123/capture_payment \
-H "Authorization: Bearer sk_your_secret_key" \
-H "Content-Type: application/json" \
-d '{
"received": "2026-03-20",
"method": "eft",
"amount": 150000,
"reference": "REF123456"
}'Returns 422 Unprocessable if the invoice is already paid or cancelled.
Apply credits to an invoice
POST /invoice/:id/apply_credits
Settles the invoice from the client's existing unallocated credit balance in the invoice's currency, oldest credit first. No money changes hands, so the invoice must already be sent (draft and scheduled invoices are not auto-promoted here - send the invoice first).
curl -X POST https://api.rebill.co.za/invoice/abc123/apply_credits \ -H "Authorization: Bearer sk_your_secret_key"
Response:
{
"allocated": 50000
}Returns 422 Unprocessable if the invoice is not yet sent, already paid or cancelled, or the client has no available credit.
List payments for an invoice
GET /invoice/:id/payment
Returns every payment allocated to this invoice, newest allocation last.
curl https://api.rebill.co.za/invoice/abc123/payment \ -H "Authorization: Bearer sk_your_secret_key"
Response:
{
"payments": [
{
"payment_id": "pay_001",
"received": "2026-03-20T00:00:00Z",
"gateway": "manual",
"method": "eft",
"reference": "REF123456",
"amount": 150000,
"type": "payment",
"source": "capture"
}
]
}| Response field | Type | Description |
|---|---|---|
| payment_id | string | ID of the underlying client payment |
| received | timestamp | Date this allocation is considered received |
| gateway | string | Payment gateway, or manual for a manually captured payment |
| method | string | Payment method |
| reference | string | Payment reference or transaction ID |
| amount | integer | Amount of the payment allocated to THIS invoice, in cents |
| type | string | payment or adjustment |
| source | string | capture (money received) or credit (settled from existing client credit) |
Delete a payment
DELETE /invoice/:id/payment/:payment_id
Deletes a manually captured payment. Because a payment is a client-level record, this deletes the whole payment and reverts every allocation it funded - on this invoice and any others it was split across, not just this one. Gateway-recorded payments cannot be deleted (returns 422 Unprocessable).
curl -X DELETE https://api.rebill.co.za/invoice/abc123/payment/pay_001 \ -H "Authorization: Bearer sk_your_secret_key"
Waive a late fee
POST /invoice/:id/waive_late_fee
Removes every late_fee line item from the invoice and recomputes its totals. Returns 404 if the invoice has no late fee lines.
curl -X POST https://api.rebill.co.za/invoice/abc123/waive_late_fee \ -H "Authorization: Bearer sk_your_secret_key"
List notifications for an invoice
GET /invoice/:id/notification
Returns the delivery history (email and WhatsApp sends) recorded for this invoice, alongside delivery/engagement state for tracked emails.
curl https://api.rebill.co.za/invoice/abc123/notification \ -H "Authorization: Bearer sk_your_secret_key"
Response:
{
"notifications": [
{
"id": "notif_001",
"created": "2026-03-15T09:00:00Z",
"entity_type": "invoice",
"entity_id": "abc123",
"channel": "email",
"event": "sent",
"recipient": "[email protected]",
"source": "user",
"tracking_token": "trk_abc"
}
],
"delivery": {
"trk_abc": { "delivered": true, "opened": true }
}
}Keys in delivery match each entry's tracking_token (empty for WhatsApp sends and anything sent before tracking existed).