Invoices
Overview
Invoices bill a customer for one or more products. Every invoice is given a sequential document number on creation, a hosted PDF download link, and an optional online payment link that lets the customer pay the invoice by card.
Invoices are also generated automatically by other parts of the platform:
- Collections create an invoice per transaction when the collection was submitted with an
invoiceobject. - Subscriptions create an invoice per billing cycle.
Automatically generated invoices are linked back to their source through transaction_id and subscription_id. They are read-only apart from their status, and invoices linked to a subscription can not be edited at all.
Invoice status
| Status | Description | Set by |
|---|---|---|
DRAFT | Not yet issued. The invoice is fully editable, has no payment link and is not visible to the customer. | You |
ISSUED | Finalised and payable. Set this when creating the invoice, when updating a draft, or automatically when the invoice is sent. | You |
PAID | Settled in full. Set on create to record an invoice that was already paid, set through update to mark an issued invoice as settled, or set automatically when a linked payment succeeds. | You / Platform |
OVERDUE | Issued and still unpaid after the due date. | Platform |
UNCOLLECTABLE | The linked collection came back unpaid or disputed. | Platform |
VOID | Cancelled after being issued. The document number is retained for audit purposes. | Platform |
NOTE
Only
DRAFT,ISSUEDandPAIDmay be set through the API.OVERDUE,UNCOLLECTABLEandVOIDare managed by the platform.Through update a
PAIDstatus may only be applied to an invoice that has already been issued; a draft must be issued first.
Payment collection
payment_collection determines how the invoice gets settled:
| Value | Description |
|---|---|
REQUEST_PAYMENT | The customer settles the invoice themselves, using the payment link on the invoice and in the invoice email. This is the default. |
AUTOCHARGE | The platform automatically charges the customer's vaulted card on the due date. |
WARNING
AUTOCHARGErequires the customer to have a card stored through the card vault API, and the payment method supplied indefault_payment_method_idmust be the one the card was vaulted against. If the customer has no vaulted card the invoice can not be charged automatically and they will have to pay through the payment link instead, so leaveis_payment_linkset to true when usingAUTOCHARGE.
Totals
Totals are always calculated by the platform from the line items and can not be supplied directly.
Each line is costed and taxed on its own, the way accounting software does it:
- The line total excluding VAT is
price×qty. - When
is_taxis true, 15% VAT is charged on that line total and rounded to the nearest cent.taxis returned on every line item; whenis_taxis false it is always0.00. - The line's
totalis its total excluding VAT plus itstax. total_exclis the sum of every line total excluding VAT,total_taxis the sum of every line'stax, andtotal_inclis the two added together.
Because VAT is rounded per line rather than on the subtotal, an invoice can differ by a cent or two from applying 15% to total_excl directly. Three lines of R10.10 each carry R1.52 of VAT, so the invoice totals R34.86 rather than the R34.85 that taxing the R30.30 subtotal would give. This matches how Xero and Sage total the same document, so invoices reconcile when they are synced across.
NOTE
Quantity is part of the line, not a repeat of it. One line of
qty3 at R10.10 is taxed once on R30.30 (R4.55 of VAT), where three separate lines ofqty1 are taxed three times on R10.10 (R4.56 in total).
total_tax and each line's tax always add up, so an invoice can be reconciled line by line. On an invoice that records a payment already collected, the amount taken is what the invoice must show, so the last line absorbs any rounding difference between it and the sum of the lines. That line's tax can therefore be a cent off 15% of its own total.
Document numbers
document_number is assigned when the invoice record is created (including drafts) and is unique and sequential per account. The format is INV-0000001, or INV-{PREFIX}-0000001 when a custom invoice prefix has been configured on your account. Numbers are never reused, including by deleted invoices.
Create
API call to create new invoices.
Path
POST /invoices/create
Request Body
{
"records": [{
"customer_id": "cus_Fjim5EFyD3mUcXWcaoOkP",
"invoice_status": "DRAFT",
"items": [{
"product_id": "pro_MsAXh25ajSIv4s6k52vQn",
"description": "Good for exercise",
"price": 45.00,
"qty": 2
}],
"is_tax": true,
"due_days": 30,
"payment_collection": "REQUEST_PAYMENT",
"default_payment_method_id": null,
"is_payment_link": true,
"memo": "Thank you for your business.",
"footer": "Please use the invoice number as your payment reference.",
"metadata": { "my_key": "my_value" }
}]
}
Recording an invoice that has already been settled:
{
"records": [{
"customer_id": "cus_Fjim5EFyD3mUcXWcaoOkP",
"invoice_status": "PAID",
"paid_at": "2026-09-07 14:32:10",
"items": [{
"product_id": "pro_MsAXh25ajSIv4s6k52vQn",
"price": 45.00,
"qty": 2
}],
"is_tax": true
}]
}
Charging a vaulted card automatically on the due date:
{
"records": [{
"customer_id": "cus_Fjim5EFyD3mUcXWcaoOkP",
"invoice_status": "ISSUED",
"payment_collection": "AUTOCHARGE",
"default_payment_method_id": "pam_YxK0zCtJpVpRzdBYTlxnm",
"items": [{
"product_id": "pro_MsAXh25ajSIv4s6k52vQn",
"price": 45.00,
"qty": 2
}],
"is_tax": true,
"due_days": 30
}]
}
Response Body
The response follows the same format as the list response
{
"status": true,
"records": [{
"id": "inv_8OPF8BRIXPfaHbXglqVAy",
"document_number": "INV-0000015",
...
}, ...]
}
Request Parameters
Field | Required | Type | Description | Example |
|---|---|---|---|---|
| *.customer_id | Y | String(32) | Customer to be invoiced, see customers. The customer must have an email address to be able to receive the invoice. | cus_Fjim5EFyD3mUcXWcaoOkP |
| *.invoice_status | N | ENUM | Status to create the invoice in. Defaults to DRAFT. Use ISSUED to finalise the invoice immediately without sending it, then send it separately. Use PAID to record an invoice that has already been settled, which issues and marks it paid in one call and lets you send the customer a receipt. (DRAFT,ISSUED,PAID) | DRAFT |
| *.paid_at | N | Datetime | When the invoice was paid. Only used with a PAID status and defaults to the current date and time. | 2026-09-07 14:32:10 |
| *.items | Y | Array | Line items being invoiced. At least one item is required. | |
| *.items. *.product_id | Y | String(32) | Line item product id, see products API. Any pricing type and billing period may be combined on an invoice. | pro_MsAXh25ajSIv4s6k52vQn |
| *.items. *.description | N | String(256) | Line item description. Defaults to the product description. | Good for exercise |
| *.items. *.price | N | Decimal | Line item price excluding VAT. Defaults to the product price. | 45.00 |
| *.items. *.qty | N | Integer | Line item quantity, minimum 1. Defaults to 1. | 2 |
| *.is_tax | N | Boolean | true - Adds 15% VAT to the invoice total. Defaults to true when your account has a VAT number configured, otherwise false. | true |
| *.due_days | N | Integer | Number of days from the invoice date until payment is due, minimum 1. Defaults to the payment terms configured on your account (30 days). | 30 |
| *.payment_collection | N | ENUM | How the invoice will be collected, see payment collection. AUTOCHARGE requires the customer to have a card stored through the card vault. Defaults to REQUEST_PAYMENT. (AUTOCHARGE,REQUEST_PAYMENT) | REQUEST_PAYMENT |
| *.default_payment_method_id | C | String(32) | Payment method used to charge the customer's vaulted card, see payment methods. Required when payment_collection is AUTOCHARGE and must match the payment method the card was vaulted against. | pam_YxK0zCtJpVpRzdBYTlxnm |
| *.is_payment_link | N | Boolean | true - Include an online payment link on the invoice and in the invoice email. Defaults to true. | true |
| *.memo | N | String(2048) | Note shown to the customer on the invoice. Defaults to the memo configured on your account. | Thank you for your business. |
| *.footer | N | String(2048) | Footer shown at the bottom of the invoice. Defaults to the footer configured on your account. | Please use the invoice number as your payment reference. |
| *.metadata | N | Object | Custom key/value data stored against the invoice. | {"my_key": "my_value"} |
Update
API call to update existing invoices.
What may be updated depends on the current status of the invoice:
- Draft invoices may be edited or deleted. Every field accepted by create may be changed, the invoice may be issued by setting
invoice_statustoISSUED, and it may be deleted by settinginvoice_statustoDELETED. - Issued and overdue invoices may only be marked as paid by setting
invoice_statustoPAID. No other field may be changed.
Path
POST /invoices/update
Request Body
Only include fields you wish to change (besides id used to find the record).
Editing a draft:
{
"records": [{
"id": "inv_8OPF8BRIXPfaHbXglqVAy",
"due_days": 14,
"items": [{
"product_id": "pro_MsAXh25ajSIv4s6k52vQn",
"price": 45.00,
"qty": 3
}]
}]
}
Deleting a draft:
{
"records": [{
"id": "inv_8OPF8BRIXPfaHbXglqVAy",
"invoice_status": "DELETED"
}]
}
Marking an issued invoice as paid:
{
"records": [{
"id": "inv_8OPF8BRIXPfaHbXglqVAy",
"invoice_status": "PAID",
"paid_at": "2026-09-07 14:32:10"
}]
}
Response Body
The response follows the same format as the list response
{
"status": true,
"records": [{
"id": "inv_8OPF8BRIXPfaHbXglqVAy",
...
}, ...]
}
Request Parameters
Field | Required | Draft only | Type | Description | Example |
|---|---|---|---|---|---|
| *.id | Y | - | String(32) | Record id to update | inv_8OPF8BRIXPfaHbXglqVAy |
| *.invoice_status | N | N | ENUM | ISSUED finalises a draft, DELETED deletes a draft, and PAID marks an issued or overdue invoice as settled. Leave blank to keep the same status. (ISSUED,PAID,DELETED) | PAID |
| *.paid_at | N | N | Datetime | When the invoice was paid. Only used with a PAID status and defaults to the current date and time. | 2026-09-07 14:32:10 |
| *.customer_id | N | Y | String(32) | Customer to be invoiced, see customers. | cus_Fjim5EFyD3mUcXWcaoOkP |
| *.items | N | Y | Array | Overwrites all line items stored on the invoice; items can not be changed individually so include every item you wish to keep. | |
| *.items. *.product_id | Y | Y | String(32) | Line item product id, see products API. | pro_MsAXh25ajSIv4s6k52vQn |
| *.items. *.description | N | Y | String(256) | Line item description. | Good for exercise |
| *.items. *.price | N | Y | Decimal | Line item price excluding VAT. | 45.00 |
| *.items. *.qty | N | Y | Integer | Line item quantity, minimum 1. | 3 |
| *.is_tax | N | Y | Boolean | true - Adds 15% VAT to the invoice total. | true |
| *.due_days | N | Y | Integer | Number of days from the invoice date until payment is due, minimum 1. | 14 |
| *.payment_collection | N | Y | ENUM | See payment collection. (AUTOCHARGE,REQUEST_PAYMENT) | REQUEST_PAYMENT |
| *.default_payment_method_id | C | Y | String(32) | Required when payment_collection is AUTOCHARGE and must match the payment method the customer's card was vaulted against. | pam_YxK0zCtJpVpRzdBYTlxnm |
| *.is_payment_link | N | Y | Boolean | true - Include an online payment link on the invoice. | true |
| *.memo | N | Y | String(2048) | Note shown to the customer on the invoice. | Thank you for your business. |
| *.footer | N | Y | String(2048) | Footer shown at the bottom of the invoice. | Please use the invoice number as your payment reference. |
| *.metadata | N | Y | Object | Custom key/value data stored against the invoice. | {"my_key": "my_value"} |
WARNING
Invoices linked to a subscription can not be edited at all, only their status may be changed.
Deleting is only possible while the invoice is a draft. An issued invoice must be voided instead so that the document number remains on record, which can be done from the Kwik dashboard.
List
List invoices loaded on the platform, including the PDF download link and the online payment link.
Path
GET /invoices/list
Query Parameters
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
| invoice_status | ENUM | N | Filter for a specific invoice status. (DRAFT,ISSUED,PAID,OVERDUE,UNCOLLECTABLE,VOID). | ISSUED |
| invoice_id | String(32) | N | Filter for a specific invoice id. | inv_8OPF8BRIXPfaHbXglqVAy |
| document_number | String(128) | N | Filter for a specific invoice document number. | INV-0000015 |
| customer_id | String(32) | N | Filter for a specific customer id. | cus_Fjim5EFyD3mUcXWcaoOkP |
| subscription_id | String(32) | N | Filter for invoices generated by a specific subscription. | sub_kFPzq4kLpuTPgOsZ2sVwB |
| transaction_id | String(32) | N | Filter for the invoice generated by a specific transaction. | tra_VLSEUZK5STgmP4J6yBDlU |
| payment_collection | ENUM | N | Filter for a specific collection method. (AUTOCHARGE,REQUEST_PAYMENT). | REQUEST_PAYMENT |
| search | String(128) | N | Search across the customer name, customer email and document number. | Superior Meteor |
| date_start | Date(YYYY-MM-DD) | N | Filter for invoices created on or after this date. | 2026-06-14 |
| date_end | Date(YYYY-MM-DD) | N | Filter for invoices created on or before this date. | 2026-10-26 |
| date_due_start | Date(YYYY-MM-DD) | N | Filter for invoices due on or after this date. | 2026-07-06 |
| date_due_end | Date(YYYY-MM-DD) | N | Filter for invoices due on or before this date. | 2026-08-04 |
| date_sent_start | Date(YYYY-MM-DD) | N | Filter for invoices sent on or after this date. | 2026-07-06 |
| date_sent_end | Date(YYYY-MM-DD) | N | Filter for invoices sent on or before this date. | 2026-08-04 |
| page | Integer | N | Page of results to return, starting at 1. Defaults to 1. | 2 |
| per_page | Integer | N | Number of invoices per page, minimum 1. Defaults to 100 and is capped at 500. | 100 |
Response Body
{
"status": true,
"pagination": {
"total": 215,
"per_page": 100,
"current_page": 1,
"last_page": 3,
"next_page": 2,
"prev_page": null
},
"records": [{
"id": "inv_8OPF8BRIXPfaHbXglqVAy",
"created_at": "2026-09-07 10:30:00",
"updated_at": null,
"deleted_at": null,
"archived_at": null,
"document_number": "INV-0000015",
"invoice_status": "ISSUED",
"customer_id": "cus_Fjim5EFyD3mUcXWcaoOkP",
"subscription_id": null,
"transaction_id": null,
"items": [{
"product_id": "pro_MsAXh25ajSIv4s6k52vQn",
"name": "Monthly Membership",
"description": "Good for exercise",
"price": 45.00,
"qty": 2,
"tax": 13.50,
"total": 103.50
}],
"is_tax": true,
"total_excl": 90.00,
"total_tax": 13.50,
"total_incl": 103.50,
"due_days": 30,
"due_date": "2026-10-07",
"payment_collection": "REQUEST_PAYMENT",
"default_payment_method_id": null,
"is_payment_link": true,
"download_link": "https://invoices.kwik.co.za/download/xDq1ZbW...",
"payment_link": "https://pay.kwik.co.za/invoice/xDq1ZbW...",
"email_cc": "accounts@mail.com",
"date_send_scheduled": null,
"date_invoice_sent": "2026-09-07 10:31:44",
"paid_at": null,
"memo": "Thank you for your business.",
"footer": "Please use the invoice number as your payment reference.",
"metadata": { "my_key": "my_value" }
}, {
...
}]
}
Response Parameters
| Field | Type | Description | Example |
|---|---|---|---|
| status | Boolean | true - success, false - error | true |
| pagination | Object | Paging detail for the current result set | |
| *.total | Integer | Total invoices matching the filters | 215 |
| *.per_page | Integer | Invoices returned per page | 100 |
| *.current_page | Integer | Page returned | 1 |
| *.last_page | Integer | Last available page | 3 |
| *.next_page | Integer | Next page, null on the last page | 2 |
| *.prev_page | Integer | Previous page, null on the first page | null |
| records | Array of objects | ||
| *.id | String(32) | Unique ID of the invoice | inv_8OPF8BRIXPfaHbXglqVAy |
| *.created_at | Datetime | Date and time the invoice was created | 2026-09-07 10:30:00 |
| *.updated_at | Datetime | Date and time the invoice was last updated | 2026-09-08 08:12:05 |
| *.deleted_at | Datetime | Date and time the draft invoice was deleted | 2026-09-09 11:00:00 |
| *.archived_at | Datetime | Date and time the invoice was archived | 2027-04-03 09:15:00 |
| *.document_number | String(128) | Sequential invoice number shown to the customer | INV-0000015 |
| *.invoice_status | ENUM | (DRAFT,ISSUED,PAID,OVERDUE,UNCOLLECTABLE,VOID) | ISSUED |
| *.customer_id | String(32) | Customer being invoiced, see customers | cus_Fjim5EFyD3mUcXWcaoOkP |
| *.subscription_id | String(32) | Subscription that generated this invoice, null when created directly | sub_kFPzq4kLpuTPgOsZ2sVwB |
| *.transaction_id | String(32) | Transaction that generated this invoice, null when created directly | tra_VLSEUZK5STgmP4J6yBDlU |
| *.items | Array of objects | Line items being invoiced | |
| *.items. *.product_id | String(32) | Line item product id, see products API | pro_MsAXh25ajSIv4s6k52vQn |
| *.items. *.name | String(128) | Line item product name at the time of invoicing | Monthly Membership |
| *.items. *.description | String(256) | Line item description | Good for exercise |
| *.items. *.price | Decimal | Line item price excluding VAT | 45.00 |
| *.items. *.qty | Integer | Line item quantity | 2 |
| *.items. *.tax | Decimal | VAT charged on this line, rounded to the nearest cent. 0.00 when is_tax is false | 13.50 |
| *.items. *.total | Decimal | Line total including VAT, being price × qty plus tax | 103.50 |
| *.is_tax | Boolean | true - 15% VAT was added to the invoice total | true |
| *.total_excl | Decimal | Invoice total excluding VAT, being the sum of every line total excluding VAT | 90.00 |
| *.total_tax | Decimal | Total VAT on the invoice, being the sum of every line's tax. 0.00 when is_tax is false | 13.50 |
| *.total_incl | Decimal | Invoice total including VAT, being total_excl plus total_tax | 103.50 |
| *.due_days | Integer | Number of days from the invoice date until payment is due | 30 |
| *.due_date | Date(YYYY-MM-DD) | Date payment is due, calculated as the created date plus the due days | 2026-10-07 |
| *.payment_collection | ENUM | See payment collection. (AUTOCHARGE,REQUEST_PAYMENT) | REQUEST_PAYMENT |
| *.default_payment_method_id | String(32) | Payment method used to charge the customer's vaulted card when payment_collection is AUTOCHARGE, see payment methods | pam_YxK0zCtJpVpRzdBYTlxnm |
| *.is_payment_link | Boolean | true - The invoice includes an online payment link | true |
| *.download_link | String | Link the customer uses to download the invoice PDF. Available on every invoice, including drafts. | https://invoices.kwik.co.za/download/xDq1ZbW... |
| *.payment_link | String | Link the customer uses to pay the invoice online. null while the invoice is a draft, when is_payment_link is false, and once the invoice is paid. | https://pay.kwik.co.za/invoice/xDq1ZbW... |
| *.email_cc | String(512) | Comma separated addresses copied on the invoice email | accounts@mail.com |
| *.date_send_scheduled | Date(YYYY-MM-DD) | Date the invoice is scheduled to be sent, null when not scheduled | 2026-09-14 |
| *.date_invoice_sent | Datetime | Date and time the invoice was last sent, null until the invoice has been sent | 2026-09-07 10:31:44 |
| *.paid_at | Datetime | Date and time the invoice was paid, null while unpaid | 2026-09-20 08:45:12 |
| *.memo | String(2048) | Note shown to the customer on the invoice | Thank you for your business. |
| *.footer | String(2048) | Footer shown at the bottom of the invoice | Please use the invoice number as your payment reference. |
| *.metadata | Object | Invoice metadata | {"my_key": "my_value"} |
Send
Email an invoice to your customer. The invoice email contains a link to view and download the invoice, and an online payment link when is_payment_link is true.
Sending a DRAFT invoice issues it, changing the status to ISSUED. Invoices that have already been sent can be sent again as a reminder, and paid invoices can be sent to provide the customer with a receipt.
Path
POST /invoices/send
Request Body
Send immediately to the customer's billing email address:
{
"records": [{
"id": "inv_8OPF8BRIXPfaHbXglqVAy"
}]
}
Send with a copy to additional addresses, scheduled for a future date:
{
"records": [{
"id": "inv_8OPF8BRIXPfaHbXglqVAy",
"cc_email": "accounts@mail.com, auditor@mail.com",
"schedule": "2026-09-14"
}]
}
Response Body
The response follows the same format as the list response
{
"status": true,
"records": [{
"id": "inv_8OPF8BRIXPfaHbXglqVAy",
"invoice_status": "ISSUED",
"date_send_scheduled": "2026-09-14",
"date_invoice_sent": null,
...
}, ...]
}
Request Parameters
Field | Required | Type | Description | Example |
|---|---|---|---|---|
| *.id | Y | String(32) | Invoice record ID to send | inv_8OPF8BRIXPfaHbXglqVAy |
| *.cc_email | N | String(512) | Comma separated list of addresses to copy on the invoice email. | accounts@mail.com, auditor@mail.com |
| *.schedule | N | Date(YYYY-MM-DD) | Date on which to send the invoice. Must be a future date. Leave this out to send the invoice immediately. | 2026-09-14 |
Recipient
The invoice is always emailed to the linked customer record, so the address can not be set per send. The billing_email on the customer is used when it is set, otherwise the customer's email is used. Update the customer through the customers API to change where invoices are delivered.
When the customer has neither a billing_email nor an email there is nowhere to send the invoice and the request fails with a validation error.
NOTE
A scheduled invoice is issued straight away but only emailed on the scheduled date, so
date_invoice_sentstaysnulluntil it goes out. Send the invoice again without ascheduleto cancel the schedule and email it immediately.