API
Invoices API
Create invoices with per-line VAT, send them with a pay link, record offline payments and cancel them.
On this page
Overview#
Create invoices from your system, let CentraPoint email them with a secure pay link, and keep them up to date as money arrives, whether through a gateway or offline (EFT, cash). Requires a plan that includes Invoicing; pay links also need the Payment links feature.
| Endpoint | Purpose |
|---|---|
POST /api/v1/invoices | Create (idempotent on externalReference) |
GET /api/v1/invoices | List, filter by customer, reference or status |
GET /api/v1/invoices/{id} | Get one invoice |
POST /api/v1/invoices/{id}/send | Email it with a pay link |
POST /api/v1/invoices/{id}/payments | Record an offline payment |
POST /api/v1/invoices/{id}/cancel | Void an unpaid invoice |
The invoice object#
{
"id": "cmg2i9n0v0005inv0001abcd",
"number": "INV-000042",
"status": "sent",
"externalReference": "ORDER-1001",
"customer": {
"id": "cmg2c0s7t0003cust0001abcd",
"externalReference": "CRM-1001",
"email": "[email protected]"
},
"currency": "ZAR",
"issueDate": "2026-09-24T22:00:00.000Z",
"dueDate": "2026-10-08T21:59:59.000Z",
"lines": [
{
"description": "Website hosting - October",
"quantity": 1,
"unitPrice": 434.78,
"taxRate": 15,
"amount": 434.78
},
{
"description": "Domain renewal (zero-rated)",
"quantity": 1,
"unitPrice": 120,
"taxRate": 0,
"amount": 120
}
],
"subtotal": 554.78,
"taxAmount": 65.22,
"total": 620,
"amountPaid": 0,
"amountDue": 620,
"paidAt": null,
"payUrl": "https://app.centrapoint.co.za/pay/Xb7kP2qR9sLm4TvWz8Yc",
"notes": "Payment due within 14 days.",
"metadata": {
"crmId": "0061x00000AbCdE"
},
"createdAt": "2026-09-25T08:05:00.000Z",
"updatedAt": "2026-09-25T08:05:00.000Z"
}| Field | Type | Description |
|---|---|---|
idrequired | string | CentraPoint invoice ID. |
numberrequired | string | Sequential invoice number, e.g. INV-000042. |
statusrequired | string | See Statuses. |
externalReferencerequired | string | null | Your ID for the invoice. |
customerrequired | object | { id, externalReference, email } |
currencyrequired | string | ISO 4217 code. |
issueDate, dueDaterequired | string | ISO 8601 UTC timestamps (dates are interpreted in South African time). |
linesrequired | object[] | description, quantity, unitPrice (excl. VAT), taxRate (%), amount (excl. VAT). |
subtotal, taxAmount, totalrequired | number | Totals excluding VAT, VAT, and including VAT. |
amountPaid, amountDuerequired | number | Paid so far, and the outstanding balance. |
paidAtrequired | string | null | When the invoice was fully paid. |
payUrlrequired | string | null | Hosted payment page for the balance. See Pay links. |
notesrequired | string | null | Printed on the invoice. |
metadatarequired | object | Your key/value data (rules). |
createdAt, updatedAtrequired | string |
Statuses#
| status | Meaning |
|---|---|
draft | Created but not sent. Nothing paid. |
sent | Sent (or payable). Nothing paid. |
overdue | Past the due date. Nothing paid. Set by a daily job. |
partially_paid | Some money received, balance outstanding (reported instead of draft/sent/overdue). |
paid | Fully paid. |
cancelled | Voided. |
Create an invoice#
/api/v1/invoices| Field | Type | Description |
|---|---|---|
customerIdoptional | string | The customer's CentraPoint ID. Send exactly one of customerId or customer. |
customeroptional | object | { "id": "…" } or { "externalReference": "…" } of an existing customer. Create customers first with the Customers API. Send exactly one of customerId or customer. |
linesrequired | object[] | 1–200 lines: description (1–1000 chars), quantity (> 0), unitPrice (≥ 0, excl. VAT), optional taxRate (0–100, default: your organisation's VAT rate). |
externalReferenceoptional | string | 1–190 chars. Makes creation idempotent; see below. |
currencyoptional | string | ISO 4217 code, default ZAR. Invalid codes are rejected with 400. No gateway is required (invoices can be paid offline), but payUrl is only created when a gateway accepts the currency. |
issueDateoptional | string (YYYY-MM-DD) | Default today. |
dueDateoptional | string (YYYY-MM-DD) | Default: today plus your invoice due-days setting. |
notesoptional | string | Up to 4000 chars. Default: your invoice terms. |
metadataoptional | object | Key/value data. |
sendoptional | boolean | Default false (draft). true creates it as sent and emails it to the customer. |
curl -X POST "https://app.centrapoint.co.za/api/v1/invoices" \
-H "Authorization: Bearer $CENTRAPOINT_API_KEY" \
-H "Idempotency-Key: 7d0c9b1e-2f4a-4c8e-b6d3-1a5e9f2c7b40" \
-H "Content-Type: application/json" \
-d '{
"customer": {
"externalReference": "CRM-1001"
},
"externalReference": "ORDER-1001",
"dueDate": "2026-10-08",
"lines": [
{
"description": "Website hosting - October",
"quantity": 1,
"unitPrice": 434.78
},
{
"description": "Domain renewal (zero-rated)",
"quantity": 1,
"unitPrice": 120,
"taxRate": 0
}
],
"metadata": {
"crmId": "0061x00000AbCdE"
},
"send": true
}'Returns 201 with the invoice object.
VAT and totals#
- Each line's
amountis quantity × unit price, rounded to cents; VAT is calculated per line at its own rate. subtotalis the sum of line amounts,taxAmountthe sum of line VAT,totaltheir sum. The total must be greater than zero.- Use
taxRate: 0for zero-rated or exempt lines.
Idempotent creation#
If an invoice with the same externalReference already exists, the request returns that invoice with 200 and changes nothing: the new body is ignored and send is not repeated. Use this to make retries safe, or combine it with an Idempotency-Key.
Pay links#
When your plan includes payment links and one of your gateways accepts the invoice currency, CentraPoint creates a single-use pay link for the outstanding balance and returns it as payUrl. The link carries the invoice's externalReference. If the balance changes (a part payment), the next send creates a fresh link for the new balance.payUrl is null for paid or cancelled invoices, and when no link could be created. If you have a Manual EFT gateway, the pay link also offers Bank transfer (EFT), and it can be created even when Manual EFT is your only gateway; approving the payer's EFT order pays the invoice (see EFT at hosted checkout).
List invoices#
/api/v1/invoices| Field | Type | Description |
|---|---|---|
customerIdoptional | string | Only this customer's invoices. |
externalReferenceoptional | string | Exact match. |
statusoptional | string | draft, sent, overdue (nothing paid yet), partially_paid, paid or cancelled. |
limitoptional | integer | 1–100, default 20. |
curl -X GET "https://app.centrapoint.co.za/api/v1/invoices?status=partially_paid" \
-H "Authorization: Bearer $CENTRAPOINT_API_KEY"Get an invoice#
/api/v1/invoices/{id}curl -X GET "https://app.centrapoint.co.za/api/v1/invoices/cmg2i9n0v0005inv0001abcd" \
-H "Authorization: Bearer $CENTRAPOINT_API_KEY"Send an invoice#
/api/v1/invoices/{id}/sendEmails the invoice to the customer, including a pay link for the balance when possible. A draft becomes sent. No body is needed. Paid and cancelled invoices can't be sent (400). Returns the updated invoice.
curl -X POST "https://app.centrapoint.co.za/api/v1/invoices/cmg2i9n0v0005inv0001abcd/send" \
-H "Authorization: Bearer $CENTRAPOINT_API_KEY" \
-H "Idempotency-Key: b21e6f0a-5c3d-4e8f-9a7b-2c1d0e3f4a56"Record an offline payment#
/api/v1/invoices/{id}/paymentsRecords money received outside CentraPoint's gateways. Payments made through a pay link are recorded automatically; don't record them again.
| Field | Type | Description |
|---|---|---|
amountrequired | number | Greater than 0 and not more than the outstanding balance. |
methodrequired | string | eft, cash, card, wallet or other. |
referenceoptional | string | Up to 190 chars, e.g. the bank statement reference. |
paidAtoptional | string (date-time) | ISO 8601 with Z or an offset. Default now; cannot be in the future. |
curl -X POST "https://app.centrapoint.co.za/api/v1/invoices/cmg2i9n0v0005inv0001abcd/payments" \
-H "Authorization: Bearer $CENTRAPOINT_API_KEY" \
-H "Idempotency-Key: c4a8e2f6-1b3d-4f5a-8c7e-9d0b2a4c6e81" \
-H "Content-Type: application/json" \
-d '{
"amount": 620,
"method": "eft",
"reference": "FNB 2026-10-02 NKOSI",
"paidAt": "2026-10-02T10:15:00+02:00"
}'Returns 201 with the updated invoice: paid when fully paid (its pay links are deactivated), otherwise partially_paid.
Cancel an invoice#
/api/v1/invoices/{id}/cancelVoids an unpaid or partially paid invoice and deactivates its pay links. Optional body { "reason": "…" } (up to 500 chars) is added to the notes. Cancelling a cancelled invoice returns it unchanged. Paid invoices can't be cancelled (400); record a refund or credit instead. Money already received on a partially paid invoice is not refunded automatically.
curl -X POST "https://app.centrapoint.co.za/api/v1/invoices/cmg2i9n0v0005inv0001abcd/cancel" \
-H "Authorization: Bearer $CENTRAPOINT_API_KEY" \
-H "Idempotency-Key: e9f1a3c5-7b2d-4e6f-8a0c-3b5d7f9a1c24" \
-H "Content-Type: application/json" \
-d '{
"reason": "Order cancelled by customer"
}'Webhook events#
invoice.paid, invoice.partially_paid and invoice.cancelled are sent to your webhook endpoints with the invoice object as data, whether the money came through a gateway or was recorded offline.
Errors#
| Status | error | When |
|---|---|---|
| 400 | invalid_request | Validation failed; invalid ISO 4217 currency; customer not found; total not above zero; unknown status filter; invoice paid/cancelled (send, payments); amount above balance; paidAt in the future. |
| 403 | plan_restricted / account_restricted | Plan lacks the API or Invoicing, or the account is read-only (writes). |
| 404 | not_found | No invoice with this ID in your organisation. |
| 409 | idempotency_conflict | Idempotency-Key reused with a different body. |
| 401 / 429 / 500 | unauthorized / rate_limited / internal_error | See Errors |