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 parameter | Type | Description |
|---|---|---|
| archived | boolean | Include archived clients (default: false) |
| code | string | Look one client up by its client code (case-insensitive). Returns a list of zero or one client, archived or not; other parameters are ignored. |
| label | string | Keep 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
| Field | Type | Description |
|---|---|---|
| id | string | Unique identifier |
| public_id | string | Short ID used in public-facing statement links |
| name | string | First name or company name |
| surname | string | Last name (if an individual) |
| business_name | string | Registered business name (optional, separate from name) |
| string | Email address | |
| bcc_email | string | Address BCC’d on every email sent to this client |
| cc_emails | array | Up to 3 additional addresses CC’d on emails to this client |
| vat_number | string | Client's VAT number, max 64 characters |
| code | string | Your own client code, max 32 characters; unique per account, case-insensitive |
| labels | array | Internal classification labels (strings), up to 10 of 40 characters each; never shown to the client |
| phone | string | Phone number in E.164 format (+27821234567) |
| billing_address | object | Billing address (see Address object) |
| shipping_address | object | Shipping address (see Address object) |
| custom_fields | object | Key-value pairs of custom fields |
| default_currency | string | ISO 4217 code used for this client’s documents when none is specified; empty falls back to your account default |
| has_password | boolean | Whether the client's statement portal is password-protected |
| invoice_prefix | string | Custom prefix for invoice numbers generated for this client |
| next_invoice_number | integer | Next sequence number for this client’s invoices |
| quote_prefix | string | Custom prefix for quote numbers generated for this client; replaces the default Q |
| next_quote_number | integer | Next sequence number for this client’s quotes |
| portal_token | string | Token used in the client’s statement portal link |
Address object
| Field | Type | Description |
|---|---|---|
| street_1 | string | Street address line 1 |
| street_2 | string | Street address line 2 |
| city | string | City |
| province | string | Province or state |
| postal_code | string | Postal code |
| country | string | Country |
Create a client
POST /client
| Field | Required | Type | Description |
|---|---|---|---|
| name | Yes | string | First name or company name |
| No | string | Valid email address | |
| surname | No | string | Last name |
| business_name | No | string | Registered business name |
| labels | No | array | Internal labels; trimmed and deduplicated case-insensitively |
| bcc_email | No | string | Address BCC’d on every email sent to this client |
| cc_emails | No | array | Up to 3 additional email addresses to CC |
| vat_number | No | string | Client's VAT number, max 64 characters |
| code | No | string | Your own client code, max 32 characters; must not be in use by another client |
| phone | No | string | E.164 format (e.g. +27821234567) |
| default_currency | No | string | ISO 4217 code; leave empty to use your account default |
| billing_address | No | object | Billing address |
| shipping_address | No | object | Shipping address |
| invoice_prefix | No | string | Custom prefix for this client's invoice numbers, must be unique per account |
| quote_prefix | No | string | Custom 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.
| Field | Required | Type | Description |
|---|---|---|---|
| clients | Yes | array | Array 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.
| Field | Required | Type | Description |
|---|---|---|---|
| source_id | Yes | string | ID of the duplicate client to merge away |
| target_id | Yes | string | ID 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
| Field | Type | Description |
|---|---|---|
| id | string | Unique identifier |
| received | timestamp | Date the payment was received |
| created | timestamp | When the payment record was created |
| gateway | string | Always 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 |
| method | string | Free-text payment method (e.g. eft, cash, card); adjustments use adjustment |
| reference | string | Free-text reference; adjustments store the reason here |
| amount | integer | Amount in cents. Positive for payments; adjustments may be negative (debit) |
| currency | string | ISO 4217 currency code |
| type | string | payment or adjustment |
| allocations | array | Only 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.
| Field | Required | Type | Description |
|---|---|---|---|
| received | Yes | date | Date the payment was received (YYYY-MM-DD) |
| method | Yes | string | Free-text payment method (e.g. eft, cash, card) |
| amount | Yes | integer | Total amount received, in cents (must be greater than 0) |
| reference | No | string | Free-text reference |
| currency | No | string | ISO 4217 code; defaults to the client's default currency, then your account default |
| allocations | No | array | Invoices 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.
| Field | Required | Type | Description |
|---|---|---|---|
| allocations | No | array | New 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.
| Field | Required | Type | Description |
|---|---|---|---|
| date | Yes | date | Date of the adjustment (YYYY-MM-DD) |
| amount | Yes | integer | Amount in cents. Positive adds credit; negative removes it |
| reason | Yes | string | Free-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 parameter | Type | Description |
|---|---|---|
| from | date | Start of the statement period (YYYY-MM-DD). Omit for full history |
| to | date | End of the statement period (YYYY-MM-DD). Omit for up to today |
| as_at | date | Show 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"