API Reference

Clients API

Create, list, update and delete clients via the Rebill API, with the client object schema, address structure, and endpoints for a client's invoices and quotes.

Last updated

Clients are the businesses or individuals you bill. Every invoice, quote, and recurring invoice must be linked to a client. You can also track credit balances and send statements per client.

List clients

GET /client

Returns all active (non-archived) clients for your account.

Query parameterTypeDescription
archivedbooleanInclude archived clients (default: false)
codestringLook one client up by its client code (case-insensitive). Returns a list of zero or one client, archived or not; other parameters are ignored.
labelstringKeep only clients carrying at least one of these labels, case-insensitively. Repeat the parameter or separate values with commas: ?label=debit-order&label=prepaid.
curl https://api.rebill.co.za/client \
  -H "Authorization: Bearer sk_your_secret_key"

Response:

{
  "clients": [
    {
      "id": "xyz789",
      "name": "Acme Corp",
      "surname": "",
      "business_name": "Acme Corp Pty Ltd",
      "email": "[email protected]",
      "cc_emails": [],
      "phone": "+27821234567",
      "vat_number": "",
      "code": "",
      "labels": [],
      "billing_address": null,
      "shipping_address": null,
      "custom_fields": {},
      "default_currency": "",
      "has_password": false,
      "invoice_prefix": "",
      "next_invoice_number": 1,
      "quote_prefix": "",
      "next_quote_number": 1
    }
  ]
}

List items do not include credit_balances; fetch a single client (below) or its statement for that.

Get a client

GET /client/:id

Returns a single client by ID, including its current credit balances.

curl https://api.rebill.co.za/client/xyz789 \
  -H "Authorization: Bearer sk_your_secret_key"

Response adds one field beyond the Client object: credit_balances (object) - the client's unallocated credit, keyed by currency code, e.g. { "ZAR": 5000 }.

Client object

FieldTypeDescription
idstringUnique identifier
public_idstringShort ID used in public-facing statement links
namestringFirst name or company name
surnamestringLast name (if an individual)
business_namestringRegistered business name (optional, separate from name)
emailstringEmail address
bcc_emailstringAddress BCC’d on every email sent to this client
cc_emailsarrayUp to 3 additional addresses CC’d on emails to this client
vat_numberstringClient's VAT number, max 64 characters
codestringYour own client code, max 32 characters; unique per account, case-insensitive
labelsarrayInternal classification labels (strings), up to 10 of 40 characters each; never shown to the client
phonestringPhone number in E.164 format (+27821234567)
billing_addressobjectBilling address (see Address object)
shipping_addressobjectShipping address (see Address object)
custom_fieldsobjectKey-value pairs of custom fields
default_currencystringISO 4217 code used for this client’s documents when none is specified; empty falls back to your account default
has_passwordbooleanWhether the client's statement portal is password-protected
invoice_prefixstringCustom prefix for invoice numbers generated for this client
next_invoice_numberintegerNext sequence number for this client’s invoices
quote_prefixstringCustom prefix for quote numbers generated for this client; replaces the default Q
next_quote_numberintegerNext sequence number for this client’s quotes
portal_tokenstringToken used in the client’s statement portal link

Address object

FieldTypeDescription
street_1stringStreet address line 1
street_2stringStreet address line 2
citystringCity
provincestringProvince or state
postal_codestringPostal code
countrystringCountry

Create a client

POST /client
FieldRequiredTypeDescription
nameYesstringFirst name or company name
emailNostringValid email address
surnameNostringLast name
business_nameNostringRegistered business name
labelsNoarrayInternal labels; trimmed and deduplicated case-insensitively
bcc_emailNostringAddress BCC’d on every email sent to this client
cc_emailsNoarrayUp to 3 additional email addresses to CC
vat_numberNostringClient's VAT number, max 64 characters
codeNostringYour own client code, max 32 characters; must not be in use by another client
phoneNostringE.164 format (e.g. +27821234567)
default_currencyNostringISO 4217 code; leave empty to use your account default
billing_addressNoobjectBilling address
shipping_addressNoobjectShipping address
invoice_prefixNostringCustom prefix for this client's invoice numbers, must be unique per account
quote_prefixNostringCustom prefix for this client's quote numbers, replacing the default Q. Invoice and quote prefixes share one namespace, so it must not match any prefix already used on the account, including this client's own invoice_prefix
curl -X POST https://api.rebill.co.za/client \
  -H "Authorization: Bearer sk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Corp",
    "email": "[email protected]",
    "phone": "+27821234567",
    "billing_address": {
      "street_1": "12 Main Road",
      "city": "Cape Town",
      "province": "Western Cape",
      "postal_code": "8001",
      "country": "South Africa"
    }
  }'

Response (201 Created):

{
  "id": "xyz789"
}

Free plan accounts are limited to 5 clients. Attempting to create more returns 402 Payment Required.

Update a client

PUT /client/:id

Updates a client. Accepts the same fields as create, plus custom_fields (object), next_invoice_number (integer, minimum 1), and next_quote_number (integer, minimum 1). Fields you omit will be cleared; send the full object.

Delete a client

DELETE /client/:id

Removes a client. If the client has any recurring invoices, they must be deleted first (returns 422 Unprocessable). If the client has invoice history, it is archived instead of deleted so its documents keep an audit trail; a client with no invoice history is hard-deleted. Returns 200 OK either way.

Bulk import clients

POST /client/import

Bulk-creates clients from a plain JSON array of rows (typically built from a CSV on your side). Validation is all-or-nothing: if any row fails, no clients are created and every error is returned.

FieldRequiredTypeDescription
clientsYesarrayArray of rows: name (required), surname, business_name, code, labels (one string, labels separated by | or ;), email, phone, street_1, street_2, city, province, postal_code, country
curl -X POST https://api.rebill.co.za/client/import \
  -H "Authorization: Bearer sk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "clients": [
      {
        "name": "Jane",
        "surname": "Doe",
        "email": "[email protected]",
        "phone": "0821234567",
        "city": "Cape Town",
        "country": "South Africa"
      }
    ]
  }'

Response (201 Created):

{
  "imported": 1
}

South African local numbers (0xxxxxxxxx) are normalised to E.164 automatically. Validation errors (400) come back as:

{
  "message": "Validation failed",
  "errors": [
    { "row": 2, "field": "email", "errors": ["must be a valid email address"] }
  ]
}

Free plan accounts are subject to the same 5-client cap as POST /client: importing more than the remaining headroom returns 402 Payment Required without creating any rows.

Update client labels

PUT /client/:id/labels

Replaces the client's internal labels and touches nothing else, so an integration that syncs classifications does not have to send the whole client. Works on archived clients too. On PUT /client/:id, omit labels to leave them unchanged or send a list to replace them.

curl -X PUT https://api.rebill.co.za/client/xyz789/labels \
  -H "Authorization: Bearer sk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{ "labels": ["debit-order", "postpaid"] }'

Response:

{
  "labels": ["debit-order", "postpaid"]
}

Labels are trimmed and deduplicated case-insensitively; more than 10, or one longer than 40 characters, returns 400. Emits client.updated.

Merge two clients

POST /client/merge

Merges a duplicate client into another. Every invoice, quote, recurring invoice, and expense on the source is reassigned to the target, and the source's payment ledger (payments, allocations, credit) is re-parented onto the target. The source client is then archived. Neither client may already be archived, and source and target must differ.

FieldRequiredTypeDescription
source_idYesstringID of the duplicate client to merge away
target_idYesstringID of the client to keep
curl -X POST https://api.rebill.co.za/client/merge \
  -H "Authorization: Bearer sk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "source_id": "dup456",
    "target_id": "xyz789"
  }'

Response:

{
  "invoices_moved": 3,
  "quotes_moved": 1,
  "recurring_invoices_moved": 0,
  "expenses_moved": 0,
  "credit_transferred": 5000
}

Emits client.updated for the target and client.deleted for the archived source.

Payment ledger

Payments are captured against a client, not directly against an invoice: you record the full amount received, then optionally allocate part or all of it to one or more issued invoices in the same request. Any amount left unallocated becomes the client's credit, tracked per currency (the same credit_balances shown on the client object). Later, POST /invoice/:id/apply_credits (documented in the Invoices section) consumes that credit to settle a specific invoice.

List payments for a client

GET /client/:id/payment

Returns every payment and adjustment recorded against the client, newest first, each annotated with the invoices it has been allocated to, plus the client's derived credit balances.

curl https://api.rebill.co.za/client/xyz789/payment \
  -H "Authorization: Bearer sk_your_secret_key"

Response:

{
  "payments": [
    {
      "id": "pay_001",
      "received": "2026-03-20T00:00:00Z",
      "created": "2026-03-20T09:12:00Z",
      "gateway": "manual",
      "method": "eft",
      "reference": "REF123456",
      "amount": 150000,
      "currency": "ZAR",
      "type": "payment",
      "allocations": [
        { "id": "alloc_001", "invoice_id": "abc123", "invoice_number": "INV-0001", "amount": 100000 }
      ]
    }
  ],
  "credit_balances": { "ZAR": 50000 }
}

Payment object

FieldTypeDescription
idstringUnique identifier
receivedtimestampDate the payment was received
createdtimestampWhen the payment record was created
gatewaystringAlways manual for payments and adjustments created via this API; gateway-settled payments (Paystack, Yoco, etc.) appear here too but are created by their own webhook flow, not by this endpoint
methodstringFree-text payment method (e.g. eft, cash, card); adjustments use adjustment
referencestringFree-text reference; adjustments store the reason here
amountintegerAmount in cents. Positive for payments; adjustments may be negative (debit)
currencystringISO 4217 currency code
typestringpayment or adjustment
allocationsarrayOnly present on the list endpoint: the invoices this payment has been allocated to, each with id, invoice_id, invoice_number, amount

Capture a payment

POST /client/:id/payment

Records a manually captured payment against the client, optionally allocating part of it to one or more issued invoices in the same request.

FieldRequiredTypeDescription
receivedYesdateDate the payment was received (YYYY-MM-DD)
methodYesstringFree-text payment method (e.g. eft, cash, card)
amountYesintegerTotal amount received, in cents (must be greater than 0)
referenceNostringFree-text reference
currencyNostringISO 4217 code; defaults to the client's default currency, then your account default
allocationsNoarrayInvoices to settle from this payment: invoice_id (required) and amount (required, cents, > 0) per entry. Leave empty to leave the full amount as unallocated credit
curl -X POST https://api.rebill.co.za/client/xyz789/payment \
  -H "Authorization: Bearer sk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "received": "2026-03-20",
    "method": "eft",
    "reference": "REF123456",
    "amount": 150000,
    "allocations": [
      { "invoice_id": "abc123", "amount": 100000 }
    ]
  }'

Response (200 OK): the created Payment object.

An allocation that exceeds the payment amount or the invoice's outstanding balance, targets an invoice in the wrong currency, or references an invoice that doesn't exist or isn't issued, returns 422 Unprocessable. Allocated invoices each trigger their own payment-received email/webhook.

Reallocate a payment

PUT /client/:id/payment/:payment_id/allocations

Atomically replaces a payment's full allocation set: existing allocations are reverted from their invoices first, then the new set is validated and applied, all in one transaction.

FieldRequiredTypeDescription
allocationsNoarrayNew allocation set: invoice_id and amount (cents) per entry. Omit or send an empty array to fully unallocate the payment back to credit
curl -X PUT https://api.rebill.co.za/client/xyz789/payment/pay_001/allocations \
  -H "Authorization: Bearer sk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "allocations": [
      { "invoice_id": "def456", "amount": 150000 }
    ]
  }'

Returns 404 Not Found if the payment doesn't exist, or 422 Unprocessable for the same validation failures as capture.

Delete a payment

DELETE /client/:id/payment/:payment_id

Removes a manually captured payment or adjustment and reverts any allocations it funded back to the invoices' outstanding balances. Only payments with gateway: "manual" can be deleted; gateway-settled payments cannot. An adjustment that has already been applied to an invoice (has allocations) must be reallocated to zero first; deleting it directly returns 422 Unprocessable.

curl -X DELETE https://api.rebill.co.za/client/xyz789/payment/pay_001 \
  -H "Authorization: Bearer sk_your_secret_key"

Record an adjustment

POST /client/:id/adjustment

Records a manual credit or debit adjustment against the client's ledger balance (for example writing off a small balance or granting goodwill credit). Unlike a payment, an adjustment is never allocated to an invoice at creation time; it only shifts the client's unallocated credit. Use the reallocate endpoint above to apply it to an invoice afterwards.

FieldRequiredTypeDescription
dateYesdateDate of the adjustment (YYYY-MM-DD)
amountYesintegerAmount in cents. Positive adds credit; negative removes it
reasonYesstringFree-text reason, max 200 characters. Stored as the reference field
curl -X POST https://api.rebill.co.za/client/xyz789/adjustment \
  -H "Authorization: Bearer sk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "date": "2026-03-20",
    "amount": 5000,
    "reason": "Goodwill credit"
  }'

Response (200 OK): the created Payment object with type adjustment. The adjustment's currency always follows the client's (or account's) default currency.

List invoices for a client

GET /client/:id/invoice

Returns all invoices for a specific client. Accepts the same include_paid and include_cancelled query parameters as GET /invoice.

List quotes for a client

GET /client/:id/quote

Returns all quotes for a specific client. Accepts the same include_accepted, include_declined, include_expired, and include_converted query parameters as GET /quote.

Client statement

GET /client/:id/statement

Returns a composed statement view for the client: your public business profile, the client, its invoices and payments within the period, credit balances, and any opening balance carried into the period.

Query parameterTypeDescription
fromdateStart of the statement period (YYYY-MM-DD). Omit for full history
todateEnd of the statement period (YYYY-MM-DD). Omit for up to today
as_atdateShow the statement as it stood on this date instead of today
curl "https://api.rebill.co.za/client/xyz789/statement?from=2026-01-01&to=2026-03-31" \
  -H "Authorization: Bearer sk_your_secret_key"

Response:

{
  "account": { "...": "your business profile" },
  "client": { "...": "Client object" },
  "invoices": [ "..." ],
  "payments": [ "..." ],
  "credit_balances": { "ZAR": 0 },
  "opening_balances": { "ZAR": 0 }
}

Send a statement

PUT /client/:id/statement/send

Emails the client's statement to their email address. Takes the same optional from and to query parameters as GET /client/:id/statement (no request body); omit both to send the client's full history. Returns 422 Unprocessable if the client has no email address.

curl -X PUT "https://api.rebill.co.za/client/xyz789/statement/send?from=2026-01-01&to=2026-03-31" \
  -H "Authorization: Bearer sk_your_secret_key"

List notifications for a client

GET /client/:id/notification

Returns the delivery history (email and WhatsApp sends, e.g. statement sends) recorded for this client, alongside delivery/engagement state for tracked emails. Same response shape as the invoice notification log.

curl https://api.rebill.co.za/client/xyz789/notification \
  -H "Authorization: Bearer sk_your_secret_key"

Was this article helpful?

Still need help?

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

Contact support