API Reference

Invoices API

Create, list, send, and mark invoices as paid via the Rebill API. Includes the full invoice object schema, line item structure, and payment recording endpoints.

Last updated

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 parameterTypeDescription
include_paidbooleanInclude paid invoices (default: false)
include_cancelledbooleanInclude 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

FieldTypeDescription
idstringUnique identifier
public_idstringShort ID used in public-facing payment links
createdtimestampWhen the invoice was created
numberstringHuman-readable invoice number (e.g. INV0001). Empty on draft invoices - see the numbering note below
currencystringISO 4217 currency code
vat_enabledbooleanWhether VAT is shown on the invoice
statusstringdraft · scheduled · sent · paid · paid_partial · cancelled
client_idstringID of the client this invoice belongs to
owner_user_idstringID of the team member this invoice is attributed to
invoice_datetimestampInvoice issue date
due_datetimestampPayment due date
itemsarrayLine items (see Line item object)
amountintegerTotal invoice amount in cents
amount_dueintegerOutstanding balance in cents
notesstringNotes printed on the invoice
bank_detailsstringBank account details for EFT payment
deposit_typestringfixed or percentage (if applicable)
deposit_valueintegerCents (fixed) or basis points (percentage)
deposit_amountintegerCalculated deposit amount in cents
discount_typestringfixed or percentage; invoice-level discount (if applicable)
discount_valueintegerCents (fixed) or basis points (percentage)
discount_amountintegerCalculated invoice-level discount in cents
custom_fieldsobjectKey-value pairs of custom fields
late_fee_disabledbooleanWhether late fees are disabled for this invoice
late_fee_last_appliedtimestampWhen a late fee was last applied (if any)
scheduled_send_attimestampWhen the invoice is scheduled to be sent (only present on scheduled invoices)
payment_termsstringPayment terms text printed on the invoice (e.g. "Net 30")
external_refstringCaller-supplied reference for cross-system deduplication
po_numberstringClient's purchase order number
labelsarrayInternal organisation labels (strings); never shown to the client
batch_idstringThe invoice batch (CSV billing run) that created this invoice, when it came from one

Line item object

FieldTypeDescription
typestringproduct · service · item
namestringLine item name
descriptionstringDescription printed on the invoice
quantitynumberNumber of units
unit_priceintegerPrice per unit in cents
vat_typestringnone · standard · zero_rated · exempt
vat_rateintegerVAT rate in basis points (1500 = 15%)
discount_typestringfixed or percentage; per-line discount (optional)
discount_valueintegerCents (fixed) or basis points (percentage)
service_datedateDate the line item was performed or delivered (optional)
item_idstringID 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
FieldRequiredTypeDescription
client_idYesstringID of an existing client
invoice_dateYesdateInvoice issue date
due_dateYesdatePayment due date
itemsYesarrayAt least one line item
currencyNostringISO 4217 code; defaults to the client, then account, default currency
notesNostringNotes to print on the invoice
bank_detailsNostringBank account details for EFT
deposit_typeNostringfixed or percentage
deposit_valueNointegerCents (fixed) or basis points (percentage)
discount_typeNostringfixed or percentage; invoice-level discount
discount_valueNointegerCents (fixed) or basis points (percentage)
custom_fieldsNoobjectAdditional key-value fields to display
late_fee_disabledNobooleanDisable late fees for this invoice (default: false)
payment_termsNostringPayment terms text, max 100 characters; defaults to your account setting
external_refNostringCaller-supplied reference for idempotency, max 200 characters. Re-creating with the same value returns the existing invoice instead of a duplicate
po_numberNostringClient's purchase order number, max 100 characters
owner_user_idNostringID of the team member to attribute this invoice to; defaults to the caller
labelsNoarrayInternal organisation labels, up to 10 per invoice, 40 characters each
scheduled_send_atNotimestampISO 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 empty number 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.

FieldRequiredTypeDescription
labelsYesarrayFull 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
FieldRequiredTypeDescription
referenceNostringYour name for the run, e.g. "September 2026" (max 100 characters)
rowsYesarrayUp to 2000 row objects; every cell is a string as it appeared in the file
rows[].client_codeOne ofstringClient code (matched first, case-insensitive)
rows[].client_emailOne ofstringClient email, when no code is given; must match exactly one client
rows[].descriptionYesstringLine text, max 200 characters
rows[].quantityNostringDefaults to 1
rows[].unit_priceYesstringMajor units, e.g. "150.00"; currency signs and thousands separators are ignored
rows[].vat_rateNostringPercent. Blank = the account rate when VAT registered, otherwise no VAT; "0" = no VAT
rows[].invoice_dateNostringYYYY-MM-DD; blank = today in the account timezone
rows[].due_dateNostringYYYY-MM-DD; blank = invoice date + 30 days
rows[].po_numberNostringMax 100 characters
rows[].item_typeNostringitem (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.

FieldRequiredTypeDescription
receivedYesdateDate the payment was received (YYYY-MM-DD)
methodYesstringPayment method (e.g. eft, cash, card, cheque)
referenceNostringPayment 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 (status paid_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.

FieldRequiredTypeDescription
receivedYesdateDate the payment was received (YYYY-MM-DD)
methodYesstringPayment method (e.g. eft, cash, card, cheque)
amountYesintegerTotal amount received, in cents
referenceNostringPayment reference or transaction ID
allocationsNoarrayExplicit 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 fieldTypeDescription
payment_idstringID of the underlying client payment
receivedtimestampDate this allocation is considered received
gatewaystringPayment gateway, or manual for a manually captured payment
methodstringPayment method
referencestringPayment reference or transaction ID
amountintegerAmount of the payment allocated to THIS invoice, in cents
typestringpayment or adjustment
sourcestringcapture (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).

Was this article helpful?

Still need help?

Our support team is happy to help you get the most out of Rebill.

Contact support