API reference
Invoices
Invoices and credit notes with their lines and payments: draft them, update them while they are still drafts, send them, cancel them, record payments, credit them, and fetch the PDF or the payment link.
Endpoints
-
GET
/invoicesList invoices -
GET
/invoices/{invoice}Get an invoice -
POST
/invoicesCreate an invoice -
PATCH
/invoices/{invoice}Update an invoice -
POST
/invoices/{invoice}/sendSend an invoice -
POST
/invoices/{invoice}/mark-sentMark as sent (no e-mail) -
POST
/invoices/{invoice}/cancelCancel an invoice -
POST
/invoices/{invoice}/paymentsRecord a payment -
POST
/invoices/{invoice}/creditCredit an invoice -
POST
/invoices/{invoice}/refundedRecord a refund -
GET
/invoices/{invoice}/pdfInvoice as PDF -
GET
/invoices/{invoice}/payment-linkGet payment link -
DELETE
/invoices/{invoice}Delete an invoice
List invoices
/api/v1/invoices
A list of invoices, newest first, with their lines and recorded payments. Filter by status, customer or change date. Use filter[status]=paid together with filter[updated_since] to pick up what has been paid since your last sync. Credit notes are in the same list (type credit_note, with negative amounts); use filter[type] to fetch only invoices or only credit notes.
- Scope
-
invoices.read— Read invoices, with their lines, amounts, payments and the customer details on them - Required feature
invoices
Query parameters
| Name | Type | Description |
|---|---|---|
limit
|
integer | Number of results per page. from 1 to 100 · default: 50 |
cursor
|
string | The next_cursor or prev_cursor from meta of the previous response. |
sort
|
string | The field to sort on; a minus sign in front means descending. Which fields you can choose and what the default is are listed with this parameter. one of: -created_at, created_at, -updated_at, updated_at · default: -created_at |
filter[status]
|
string | Only invoices with this status, for example paid, overdue or credited. one of: draft, sent, viewed, paid, partial, overdue, cancelled, refunded, credited |
filter[type]
|
string | Only invoices (invoice) or only credit notes (credit_note). Without this filter you get both. one of: invoice, credit_note |
filter[customer_id]
|
string (uuid) | Only what belongs to this customer. |
filter[updated_since]
|
string (date-time) | Only what changed since this moment: ISO 8601 with a time zone, for example 2026-09-14T10:15:00Z. Useful for synchronising. Overlap your previous moment by a few minutes, because the clock of your system and that of Klantly do not run exactly in sync, and because a change made just after your previous moment would otherwise fall just outside it. The boundary is inclusive: something changed exactly at this moment is included. |
Example request
curl "https://app.klantly.com/api/v1/invoices?filter[status]=paid&sort=-updated_at" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'invoices', [
'query' => [
'filter[status]' => 'paid',
'sort' => '-updated_at',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/invoices?filter[status]=paid&sort=-updated_at', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/invoices",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
params={
"filter[status]": "paid",
"sort": "-updated_at"
},
)
data = response.json()["data"]Response 200
The response is a list with cursor pagination: data contains the objects, meta the pagination.
{
"data": [
{
"object": "invoice",
"id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
"number": "FAC-00042",
"type": "invoice",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"quote_id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"deal_id": null,
"language": "nl",
"currency": "EUR",
"invoice_date": "2026-09-16",
"due_date": "2026-10-16",
"payment_term_days": 30,
"is_term_invoice": false,
"term_percentage": null,
"term_amount": null,
"terms_conditions": null,
"notes": null,
"outro_text": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"amount_paid": "0.00",
"amount_due": "2964.50",
"sent_at": null,
"viewed_at": null,
"paid_at": null,
"cancelled_at": null,
"credited_invoice_id": null,
"credited_invoice_number": null,
"credit_reason": null,
"refund_amount": null,
"refunded_at": null,
"credit_note_state": null,
"credited_amount": "0.00",
"credit_note_ids": [],
"items": [
{
"object": "invoice_item",
"id": "9d3f87ac-d2b4-4d9b-8aef-f4a2b3c4d5ee",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00",
"credited_item_id": null
}
],
"payments": [
{
"object": "invoice_payment",
"id": "9d3f88cd-e3c5-4eac-9bf0-a5b3c4d5e6ff",
"amount": "2964.50",
"type": "full",
"status": "paid",
"payment_method": "bank_transfer",
"description": null,
"paid_at": "2026-09-16T10:15:00Z",
"created_at": "2026-09-16T10:15:00Z"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
],
"meta": {
"limit": 50,
"next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
"prev_cursor": null
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid.
Get an invoice
/api/v1/invoices/{invoice}
One invoice by id, with its lines, amounts and payments. The response carries an ETag you can send back in If-Match when updating.
- Scope
-
invoices.read— Read invoices, with their lines, amounts, payments and the customer details on them - Required feature
invoices
Path parameters
| Name | Type | Description |
|---|---|---|
invoice required |
string (uuid) | The id (UUID) of the invoice. |
Example request
curl "https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "invoice",
"id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
"number": "FAC-00042",
"type": "invoice",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"quote_id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"deal_id": null,
"language": "nl",
"currency": "EUR",
"invoice_date": "2026-09-16",
"due_date": "2026-10-16",
"payment_term_days": 30,
"is_term_invoice": false,
"term_percentage": null,
"term_amount": null,
"terms_conditions": null,
"notes": null,
"outro_text": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"amount_paid": "0.00",
"amount_due": "2964.50",
"sent_at": null,
"viewed_at": null,
"paid_at": null,
"cancelled_at": null,
"credited_invoice_id": null,
"credited_invoice_number": null,
"credit_reason": null,
"refund_amount": null,
"refunded_at": null,
"credit_note_state": null,
"credited_amount": "0.00",
"credit_note_ids": [],
"items": [
{
"object": "invoice_item",
"id": "9d3f87ac-d2b4-4d9b-8aef-f4a2b3c4d5ee",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00",
"credited_item_id": null
}
],
"payments": [
{
"object": "invoice_payment",
"id": "9d3f88cd-e3c5-4eac-9bf0-a5b3c4d5e6ff",
"amount": "2964.50",
"type": "full",
"status": "paid",
"payment_method": "bank_transfer",
"description": null,
"paid_at": "2026-09-16T10:15:00Z",
"created_at": "2026-09-16T10:15:00Z"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found.
Create an invoice
/api/v1/invoices
Creates an invoice for a customer, always as a draft. Klantly assigns the number and calculates the totals; name, address and contact details come from the customer. Use quote_id to link it to a quote.
- Scope
-
invoices.write— Create invoices (also from a quote) and update them (drafts only), cancel them, record payments, credit them and record a refund - Required feature
invoices
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Body (JSON)
| Field | Type | Description |
|---|---|---|
customer_id
required
|
string (uuid) | The customer. Required when creating; name, address and contact details come from the customer. |
title
required
|
string | Title of the invoice. Required when creating. at most 255 characters |
items
required
|
array | The lines (up to 200). When sending: a list with name (required) per line and optionally type, description, sku, quantity, unit, unit_price, unit_price_incl, discount_percentage, discount_amount, discount_description, tax_rate and is_taxable. They replace all existing lines. |
quote_id
optional
|
string (uuid) | The quote this invoice came from, or null. can be empty (null) |
description
optional
|
string | Short description; shown above the lines. can be empty (null) · at most 20000 characters |
language
optional
|
string | Language of the invoice: nl, en, de or fr. one of: nl, en, de, fr |
invoice_date
optional
|
string (date) | Invoice date (YYYY-MM-DD). |
due_date
optional
|
string (date) | Due date (YYYY-MM-DD). Leave it out and it is the invoice date plus the payment term. |
payment_term_days
optional
|
integer | Payment term in days. Without one, the company default applies. from 0 to 365 |
terms_conditions
optional
|
string | Terms shown on the invoice. can be empty (null) · at most 20000 characters |
notes
optional
|
string | Notes for the customer on the invoice. When recording a payment: a note about that payment. can be empty (null) · at most 20000 characters |
outro_text
optional
|
string | Closing text below the lines. can be empty (null) · at most 20000 characters |
hide_line_amounts
optional
|
boolean | Hides the per-line amounts on the invoice; the customer only sees the total. |
discount_percentage
optional
|
number | Discount on the total, as a percentage. from 0 to 100 |
discount_amount
optional
|
number | Discount on the total, as an amount. from 0 to 9999999 |
discount_description
optional
|
string | Why the discount was given; shown on the invoice. can be empty (null) · at most 500 characters |
Example request
curl -X POST "https://app.klantly.com/api/v1/invoices" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f" \
-d '{
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"title": "Veranda 400 × 300",
"items": [
{
"name": "Veranda",
"quantity": 1,
"unit_price": "2450.00",
"tax_rate": "21.00"
}
]
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'invoices', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'customer_id' => '9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70',
'title' => 'Veranda 400 × 300',
'items' => [
0 => [
'name' => 'Veranda',
'quantity' => 1,
'unit_price' => '2450.00',
'tax_rate' => '21.00',
],
],
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/invoices', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
body: JSON.stringify({
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"title": "Veranda 400 × 300",
"items": [
{
"name": "Veranda",
"quantity": 1,
"unit_price": "2450.00",
"tax_rate": "21.00"
}
]
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/invoices",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
json={
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"title": "Veranda 400 × 300",
"items": [
{
"name": "Veranda",
"quantity": 1,
"unit_price": "2450.00",
"tax_rate": "21.00"
}
]
},
)
data = response.json()["data"]Response 201
{
"data": {
"object": "invoice",
"id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
"number": "FAC-00042",
"type": "invoice",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"quote_id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"deal_id": null,
"language": "nl",
"currency": "EUR",
"invoice_date": "2026-09-16",
"due_date": "2026-10-16",
"payment_term_days": 30,
"is_term_invoice": false,
"term_percentage": null,
"term_amount": null,
"terms_conditions": null,
"notes": null,
"outro_text": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"amount_paid": "0.00",
"amount_due": "2964.50",
"sent_at": null,
"viewed_at": null,
"paid_at": null,
"cancelled_at": null,
"credited_invoice_id": null,
"credited_invoice_number": null,
"credit_reason": null,
"refund_amount": null,
"refunded_at": null,
"credit_note_state": null,
"credited_amount": "0.00",
"credit_note_ids": [],
"items": [
{
"object": "invoice_item",
"id": "9d3f87ac-d2b4-4d9b-8aef-f4a2b3c4d5ee",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00",
"credited_item_id": null
}
],
"payments": [
{
"object": "invoice_payment",
"id": "9d3f88cd-e3c5-4eac-9bf0-a5b3c4d5e6ff",
"amount": "2964.50",
"type": "full",
"status": "paid",
"payment_method": "bank_transfer",
"description": null,
"paid_at": "2026-09-16T10:15:00Z",
"created_at": "2026-09-16T10:15:00Z"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid. -
422
unknown_field— The input contains an unknown field. -
415
unsupported_media_type— This format is not supported. -
413
payload_too_large— The request body is too large. -
403
limit_reached— The subscription limit has been reached. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Update an invoice
/api/v1/invoices/{invoice}
Changes only the fields you send. items replaces all lines as a whole. Only a draft can still change: once the invoice has been sent it sits in the customer's bookkeeping, and you get a 409.
- Scope
-
invoices.write— Create invoices (also from a quote) and update them (drafts only), cancel them, record payments, credit them and record a refund - Required feature
invoices
Send the ETag in If-Match and you will never accidentally overwrite a newer version.
Path parameters
| Name | Type | Description |
|---|---|---|
invoice required |
string (uuid) | The id (UUID) of the invoice. |
Body (JSON)
| Field | Type | Description |
|---|---|---|
title
optional
|
string | Title of the invoice. Required when creating. at most 255 characters |
description
optional
|
string | Short description; shown above the lines. can be empty (null) · at most 20000 characters |
language
optional
|
string | Language of the invoice: nl, en, de or fr. one of: nl, en, de, fr |
invoice_date
optional
|
string (date) | Invoice date (YYYY-MM-DD). |
due_date
optional
|
string (date) | Due date (YYYY-MM-DD). Leave it out and it is the invoice date plus the payment term. |
payment_term_days
optional
|
integer | Payment term in days. Without one, the company default applies. from 0 to 365 |
terms_conditions
optional
|
string | Terms shown on the invoice. can be empty (null) · at most 20000 characters |
notes
optional
|
string | Notes for the customer on the invoice. When recording a payment: a note about that payment. can be empty (null) · at most 20000 characters |
outro_text
optional
|
string | Closing text below the lines. can be empty (null) · at most 20000 characters |
hide_line_amounts
optional
|
boolean | Hides the per-line amounts on the invoice; the customer only sees the total. |
discount_percentage
optional
|
number | Discount on the total, as a percentage. from 0 to 100 |
discount_amount
optional
|
number | Discount on the total, as an amount. from 0 to 9999999 |
discount_description
optional
|
string | Why the discount was given; shown on the invoice. can be empty (null) · at most 500 characters |
items
optional
|
array | The lines (up to 200). When sending: a list with name (required) per line and optionally type, description, sku, quantity, unit, unit_price, unit_price_incl, discount_percentage, discount_amount, discount_description, tax_rate and is_taxable. They replace all existing lines. |
Example request
curl -X PATCH "https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"notes": "Graag betalen binnen 14 dagen."
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('PATCH', 'invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', [
'json' => [
'notes' => 'Graag betalen binnen 14 dagen.',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"notes": "Graag betalen binnen 14 dagen."
}),
});
const { data } = await response.json();import os
import requests
response = requests.patch(
"https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
json={
"notes": "Graag betalen binnen 14 dagen."
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "invoice",
"id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
"number": "FAC-00042",
"type": "invoice",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"quote_id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"deal_id": null,
"language": "nl",
"currency": "EUR",
"invoice_date": "2026-09-16",
"due_date": "2026-10-16",
"payment_term_days": 30,
"is_term_invoice": false,
"term_percentage": null,
"term_amount": null,
"terms_conditions": null,
"notes": null,
"outro_text": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"amount_paid": "0.00",
"amount_due": "2964.50",
"sent_at": null,
"viewed_at": null,
"paid_at": null,
"cancelled_at": null,
"credited_invoice_id": null,
"credited_invoice_number": null,
"credit_reason": null,
"refund_amount": null,
"refunded_at": null,
"credit_note_state": null,
"credited_amount": "0.00",
"credit_note_ids": [],
"items": [
{
"object": "invoice_item",
"id": "9d3f87ac-d2b4-4d9b-8aef-f4a2b3c4d5ee",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00",
"credited_item_id": null
}
],
"payments": [
{
"object": "invoice_payment",
"id": "9d3f88cd-e3c5-4eac-9bf0-a5b3c4d5e6ff",
"amount": "2964.50",
"type": "full",
"status": "paid",
"payment_method": "bank_transfer",
"description": null,
"paid_at": "2026-09-16T10:15:00Z",
"created_at": "2026-09-16T10:15:00Z"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid. -
422
unknown_field— The input contains an unknown field. -
415
unsupported_media_type— This format is not supported. -
413
payload_too_large— The request body is too large. -
404
not_found— Not found. -
412
precondition_failed— The record has been changed in the meantime. -
409
invalid_state_transition— This action is not possible in the current status.
Send an invoice
/api/v1/invoices/{invoice}/send
Emails the invoice to the customer using the company's notification template and sets the status to sent. Sending again counts as a reminder: reminder_count goes up. For a credit note or a (partly) credited invoice the status stays; only sent_at is updated.
- Scope
-
invoices.send— Email invoices to customers and fetch the payment link for the customer - Required feature
invoices
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
invoice required |
string (uuid) | The id (UUID) of the invoice. |
Body (JSON)
| Field | Type | Description |
|---|---|---|
message
optional
|
string | Personal message in the email to the customer. can be empty (null) · at most 5000 characters |
Example request
curl -X POST "https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/send" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f" \
-d '{
"message": "Bijgaand de factuur voor de geplaatste veranda."
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/send', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'message' => 'Bijgaand de factuur voor de geplaatste veranda.',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/send', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
body: JSON.stringify({
"message": "Bijgaand de factuur voor de geplaatste veranda."
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/send",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
json={
"message": "Bijgaand de factuur voor de geplaatste veranda."
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "invoice",
"id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
"number": "FAC-00042",
"type": "invoice",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"quote_id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"deal_id": null,
"language": "nl",
"currency": "EUR",
"invoice_date": "2026-09-16",
"due_date": "2026-10-16",
"payment_term_days": 30,
"is_term_invoice": false,
"term_percentage": null,
"term_amount": null,
"terms_conditions": null,
"notes": null,
"outro_text": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"amount_paid": "0.00",
"amount_due": "2964.50",
"sent_at": null,
"viewed_at": null,
"paid_at": null,
"cancelled_at": null,
"credited_invoice_id": null,
"credited_invoice_number": null,
"credit_reason": null,
"refund_amount": null,
"refunded_at": null,
"credit_note_state": null,
"credited_amount": "0.00",
"credit_note_ids": [],
"items": [
{
"object": "invoice_item",
"id": "9d3f87ac-d2b4-4d9b-8aef-f4a2b3c4d5ee",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00",
"credited_item_id": null
}
],
"payments": [
{
"object": "invoice_payment",
"id": "9d3f88cd-e3c5-4eac-9bf0-a5b3c4d5e6ff",
"amount": "2964.50",
"type": "full",
"status": "paid",
"payment_method": "bank_transfer",
"description": null,
"paid_at": "2026-09-16T10:15:00Z",
"created_at": "2026-09-16T10:15:00Z"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid. -
422
unknown_field— The input contains an unknown field. -
415
unsupported_media_type— This format is not supported. -
413
payload_too_large— The request body is too large. -
404
not_found— Not found. -
403
forbidden— Access denied. -
409
invalid_state_transition— This action is not possible in the current status. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Mark as sent (no e-mail)
/api/v1/invoices/{invoice}/mark-sent
Moves a draft to sent without Klantly e-mailing the customer, like the button in the app. Use this when you delivered the invoice yourself: by your own e-mail, on paper or through your accounting software. sent_at is filled and the invoice.sent event follows. After that you can record a payment on it. If the invoice is already sent or further along, it is returned unchanged; a cancelled draft gives 409. Send an Idempotency-Key.
- Scope
-
invoices.write— Create invoices (also from a quote) and update them (drafts only), cancel them, record payments, credit them and record a refund - Required feature
invoices
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
invoice required |
string (uuid) | The id (UUID) of the invoice. |
Example request
curl -X POST "https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/mark-sent" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/mark-sent', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/mark-sent', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/mark-sent",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "invoice",
"id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
"number": "FAC-00042",
"type": "invoice",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"quote_id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"deal_id": null,
"language": "nl",
"currency": "EUR",
"invoice_date": "2026-09-16",
"due_date": "2026-10-16",
"payment_term_days": 30,
"is_term_invoice": false,
"term_percentage": null,
"term_amount": null,
"terms_conditions": null,
"notes": null,
"outro_text": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"amount_paid": "0.00",
"amount_due": "2964.50",
"sent_at": null,
"viewed_at": null,
"paid_at": null,
"cancelled_at": null,
"credited_invoice_id": null,
"credited_invoice_number": null,
"credit_reason": null,
"refund_amount": null,
"refunded_at": null,
"credit_note_state": null,
"credited_amount": "0.00",
"credit_note_ids": [],
"items": [
{
"object": "invoice_item",
"id": "9d3f87ac-d2b4-4d9b-8aef-f4a2b3c4d5ee",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00",
"credited_item_id": null
}
],
"payments": [
{
"object": "invoice_payment",
"id": "9d3f88cd-e3c5-4eac-9bf0-a5b3c4d5e6ff",
"amount": "2964.50",
"type": "full",
"status": "paid",
"payment_method": "bank_transfer",
"description": null,
"paid_at": "2026-09-16T10:15:00Z",
"created_at": "2026-09-16T10:15:00Z"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
409
invalid_state_transition— This action is not possible in the current status. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Cancel an invoice
/api/v1/invoices/{invoice}/cancel
Sets the invoice to cancelled. A paid or (partly) credited invoice cannot be cancelled (409); correct a sent invoice with Credit an invoice.
- Scope
-
invoices.write— Create invoices (also from a quote) and update them (drafts only), cancel them, record payments, credit them and record a refund - Required feature
invoices
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
invoice required |
string (uuid) | The id (UUID) of the invoice. |
Example request
curl -X POST "https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/cancel" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/cancel', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/cancel', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/cancel",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "invoice",
"id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
"number": "FAC-00042",
"type": "invoice",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"quote_id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"deal_id": null,
"language": "nl",
"currency": "EUR",
"invoice_date": "2026-09-16",
"due_date": "2026-10-16",
"payment_term_days": 30,
"is_term_invoice": false,
"term_percentage": null,
"term_amount": null,
"terms_conditions": null,
"notes": null,
"outro_text": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"amount_paid": "0.00",
"amount_due": "2964.50",
"sent_at": null,
"viewed_at": null,
"paid_at": null,
"cancelled_at": null,
"credited_invoice_id": null,
"credited_invoice_number": null,
"credit_reason": null,
"refund_amount": null,
"refunded_at": null,
"credit_note_state": null,
"credited_amount": "0.00",
"credit_note_ids": [],
"items": [
{
"object": "invoice_item",
"id": "9d3f87ac-d2b4-4d9b-8aef-f4a2b3c4d5ee",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00",
"credited_item_id": null
}
],
"payments": [
{
"object": "invoice_payment",
"id": "9d3f88cd-e3c5-4eac-9bf0-a5b3c4d5e6ff",
"amount": "2964.50",
"type": "full",
"status": "paid",
"payment_method": "bank_transfer",
"description": null,
"paid_at": "2026-09-16T10:15:00Z",
"created_at": "2026-09-16T10:15:00Z"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
409
invalid_state_transition— This action is not possible in the current status. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Record a payment
/api/v1/invoices/{invoice}/payments
Records a payment that came in outside Klantly, for example a bank transfer. The amount can never be higher than what is still due. If the invoice is settled in full it moves to paid and the invoice.paid event follows.
- Scope
-
invoices.write— Create invoices (also from a quote) and update them (drafts only), cancel them, record payments, credit them and record a refund - Required feature
invoices
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
invoice required |
string (uuid) | The id (UUID) of the invoice. |
Body (JSON)
| Field | Type | Description |
|---|---|---|
amount
required
|
number | The amount of the payment you are recording. Never more than what is outstanding. from 0 to 9999999 |
payment_method
optional
|
string | How it was paid: bank_transfer, cash, card, ideal, credit_card, paypal, bancontact or other. one of: bank_transfer, cash, card, ideal, credit_card, paypal, bancontact, other |
reference
optional
|
string | Your own reference for the payment, for example the bank statement or transaction id. can be empty (null) · at most 255 characters |
notes
optional
|
string | Notes for the customer on the invoice. When recording a payment: a note about that payment. can be empty (null) · at most 1000 characters |
Example request
curl -X POST "https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/payments" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f" \
-d '{
"amount": "2964.50",
"payment_method": "bank_transfer",
"reference": "NL02ABNA0123456789"
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/payments', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'amount' => '2964.50',
'payment_method' => 'bank_transfer',
'reference' => 'NL02ABNA0123456789',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/payments', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
body: JSON.stringify({
"amount": "2964.50",
"payment_method": "bank_transfer",
"reference": "NL02ABNA0123456789"
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/payments",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
json={
"amount": "2964.50",
"payment_method": "bank_transfer",
"reference": "NL02ABNA0123456789"
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "invoice",
"id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
"number": "FAC-00042",
"type": "invoice",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"quote_id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"deal_id": null,
"language": "nl",
"currency": "EUR",
"invoice_date": "2026-09-16",
"due_date": "2026-10-16",
"payment_term_days": 30,
"is_term_invoice": false,
"term_percentage": null,
"term_amount": null,
"terms_conditions": null,
"notes": null,
"outro_text": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"amount_paid": "0.00",
"amount_due": "2964.50",
"sent_at": null,
"viewed_at": null,
"paid_at": null,
"cancelled_at": null,
"credited_invoice_id": null,
"credited_invoice_number": null,
"credit_reason": null,
"refund_amount": null,
"refunded_at": null,
"credit_note_state": null,
"credited_amount": "0.00",
"credit_note_ids": [],
"items": [
{
"object": "invoice_item",
"id": "9d3f87ac-d2b4-4d9b-8aef-f4a2b3c4d5ee",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00",
"credited_item_id": null
}
],
"payments": [
{
"object": "invoice_payment",
"id": "9d3f88cd-e3c5-4eac-9bf0-a5b3c4d5e6ff",
"amount": "2964.50",
"type": "full",
"status": "paid",
"payment_method": "bank_transfer",
"description": null,
"paid_at": "2026-09-16T10:15:00Z",
"created_at": "2026-09-16T10:15:00Z"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid. -
422
unknown_field— The input contains an unknown field. -
415
unsupported_media_type— This format is not supported. -
413
payload_too_large— The request body is too large. -
404
not_found— Not found. -
409
invalid_state_transition— This action is not possible in the current status. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Credit an invoice
/api/v1/invoices/{invoice}/credit
Creates a credit note on a sent or paid invoice, like the button in the app. mode=full credits everything not yet credited, mode=lines a quantity per line (lines[].item_id and lines[].quantity) and mode=amount a free amount including VAT at a VAT rate that appears on the invoice. The response (201) is the credit note: an invoice with type credit_note, negative lines and totals and credited_invoice_id. The part that can be offset against the open amount is offset; the rest is on the credit note as refund_amount and has to be paid back to the customer. Crediting more than is left on the invoice gives 422 on amount, lines or lines.N.quantity; a draft, cancelled or fully credited invoice, or a credit note, gives 409. The events invoice.created (the credit note) and invoice.credited (the invoice) follow. Always send an Idempotency-Key.
- Scope
-
invoices.write— Create invoices (also from a quote) and update them (drafts only), cancel them, record payments, credit them and record a refund - Required feature
invoices
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
invoice required |
string (uuid) | The id (UUID) of the invoice. |
Body (JSON)
| Field | Type | Description |
|---|---|---|
mode
required
|
string | What you credit: full (everything not yet credited), lines (a quantity per line) or amount (a free amount). one of: full, lines, amount |
reason
required
|
string | The reason for the credit (required, at most 500 characters); shown on the credit note. at most 500 characters |
lines
optional
|
array | With mode=lines: the lines you credit, each with item_id and quantity. |
amount
optional
|
number | With mode=amount: the amount including VAT you credit. Never more than can still be credited on the invoice. from 0 to 9999999 |
tax_rate
optional
|
number | With mode=amount: the VAT rate of that amount. Must appear on the invoice; without it, the first rate of the invoice. can be empty (null) · from 0 to 100 |
invoice_date
optional
|
string (date) | Date of the credit note (YYYY-MM-DD); defaults to today. |
Example request
curl -X POST "https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/credit" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f" \
-d '{
"mode": "amount",
"reason": "Korting na klacht over de montage",
"amount": "150.00",
"tax_rate": "21.00"
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/credit', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'mode' => 'amount',
'reason' => 'Korting na klacht over de montage',
'amount' => '150.00',
'tax_rate' => '21.00',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/credit', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
body: JSON.stringify({
"mode": "amount",
"reason": "Korting na klacht over de montage",
"amount": "150.00",
"tax_rate": "21.00"
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/credit",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
json={
"mode": "amount",
"reason": "Korting na klacht over de montage",
"amount": "150.00",
"tax_rate": "21.00"
},
)
data = response.json()["data"]Response 201
{
"data": {
"object": "invoice",
"id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
"number": "FAC-00042",
"type": "invoice",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"quote_id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"deal_id": null,
"language": "nl",
"currency": "EUR",
"invoice_date": "2026-09-16",
"due_date": "2026-10-16",
"payment_term_days": 30,
"is_term_invoice": false,
"term_percentage": null,
"term_amount": null,
"terms_conditions": null,
"notes": null,
"outro_text": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"amount_paid": "0.00",
"amount_due": "2964.50",
"sent_at": null,
"viewed_at": null,
"paid_at": null,
"cancelled_at": null,
"credited_invoice_id": null,
"credited_invoice_number": null,
"credit_reason": null,
"refund_amount": null,
"refunded_at": null,
"credit_note_state": null,
"credited_amount": "0.00",
"credit_note_ids": [],
"items": [
{
"object": "invoice_item",
"id": "9d3f87ac-d2b4-4d9b-8aef-f4a2b3c4d5ee",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00",
"credited_item_id": null
}
],
"payments": [
{
"object": "invoice_payment",
"id": "9d3f88cd-e3c5-4eac-9bf0-a5b3c4d5e6ff",
"amount": "2964.50",
"type": "full",
"status": "paid",
"payment_method": "bank_transfer",
"description": null,
"paid_at": "2026-09-16T10:15:00Z",
"created_at": "2026-09-16T10:15:00Z"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid. -
422
unknown_field— The input contains an unknown field. -
415
unsupported_media_type— This format is not supported. -
413
payload_too_large— The request body is too large. -
404
not_found— Not found. -
409
invalid_state_transition— This action is not possible in the current status. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Record a refund
/api/v1/invoices/{invoice}/refunded
Records that the part of a credit note to be paid back (refund_amount) has been transferred to the customer; refunded_at is filled and credit_note_state becomes refunded. Only on a credit note with credit_note_state refund_pending, otherwise 409. With via=mollie, Klantly actually refunds the amount through Mollie (only if the invoice was sufficiently paid via Mollie, otherwise 409); if Mollie refuses, 422 on refund. After a partial refund credit_note_state stays refund_pending and a new call only refunds the rest.
- Scope
-
invoices.write— Create invoices (also from a quote) and update them (drafts only), cancel them, record payments, credit them and record a refund - Required feature
invoices
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
invoice required |
string (uuid) | The id (UUID) of the invoice. |
Body (JSON)
| Field | Type | Description |
|---|---|---|
via
optional
|
string | manual (default): only record that you refunded it yourself. mollie: actually refund through the Mollie payment(s) of the original invoice. one of: manual, mollie |
payment_method
optional
|
string | How the money was paid back: bank_transfer, cash, card, ideal, credit_card, paypal, bancontact or other. one of: bank_transfer, cash, card, ideal, credit_card, paypal, bancontact, other |
reference
optional
|
string | Your own reference for the refund, for example the bank statement. can be empty (null) · at most 255 characters |
Example request
curl -X POST "https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/refunded" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f" \
-d '{
"payment_method": "bank_transfer",
"reference": "NL02ABNA0123456789"
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/refunded', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'payment_method' => 'bank_transfer',
'reference' => 'NL02ABNA0123456789',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/refunded', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
body: JSON.stringify({
"payment_method": "bank_transfer",
"reference": "NL02ABNA0123456789"
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/refunded",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
json={
"payment_method": "bank_transfer",
"reference": "NL02ABNA0123456789"
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "invoice",
"id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
"number": "FAC-00042",
"type": "invoice",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"quote_id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"deal_id": null,
"language": "nl",
"currency": "EUR",
"invoice_date": "2026-09-16",
"due_date": "2026-10-16",
"payment_term_days": 30,
"is_term_invoice": false,
"term_percentage": null,
"term_amount": null,
"terms_conditions": null,
"notes": null,
"outro_text": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"amount_paid": "0.00",
"amount_due": "2964.50",
"sent_at": null,
"viewed_at": null,
"paid_at": null,
"cancelled_at": null,
"credited_invoice_id": null,
"credited_invoice_number": null,
"credit_reason": null,
"refund_amount": null,
"refunded_at": null,
"credit_note_state": null,
"credited_amount": "0.00",
"credit_note_ids": [],
"items": [
{
"object": "invoice_item",
"id": "9d3f87ac-d2b4-4d9b-8aef-f4a2b3c4d5ee",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00",
"credited_item_id": null
}
],
"payments": [
{
"object": "invoice_payment",
"id": "9d3f88cd-e3c5-4eac-9bf0-a5b3c4d5e6ff",
"amount": "2964.50",
"type": "full",
"status": "paid",
"payment_method": "bank_transfer",
"description": null,
"paid_at": "2026-09-16T10:15:00Z",
"created_at": "2026-09-16T10:15:00Z"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid. -
422
unknown_field— The input contains an unknown field. -
415
unsupported_media_type— This format is not supported. -
413
payload_too_large— The request body is too large. -
404
not_found— Not found. -
409
conflict— This conflicts with the current state. -
409
invalid_state_transition— This action is not possible in the current status. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Invoice as PDF
/api/v1/invoices/{invoice}/pdf
Returns the invoice as a PDF, exactly as the customer receives it: in the company's house style and in the language of the invoice. Useful to store it with the entry in your accounting system. The first request renders it; after that it comes from the cache until the invoice changes. Counts towards the heavy limit.
- Scope
-
invoices.read— Read invoices, with their lines, amounts, payments and the customer details on them - Required feature
invoices
Path parameters
| Name | Type | Description |
|---|---|---|
invoice required |
string (uuid) | The id (UUID) of the invoice. |
Example request
curl "https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/pdf" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-o document.pdf$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/pdf');
file_put_contents('document.pdf', (string) $response->getBody());import { writeFile } from 'node:fs/promises';
const response = await fetch('https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/pdf', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
await writeFile('document.pdf', Buffer.from(await response.arrayBuffer()));import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/pdf",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
with open("document.pdf", "wb") as pdf:
pdf.write(response.content)Response 200
The response is the file itself (application/pdf), not JSON. Content-Disposition holds the file name.
Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found.
Get payment link
/api/v1/invoices/{invoice}/payment-link
Returns the page where the customer pays the invoice online with Mollie, for you to share yourself, for example in your own reminder or via WhatsApp. With a payment schedule, the customer picks the next instalment there. Only for a sent invoice that has not been paid in full (otherwise 409 invalid_state_transition) and when the company has connected Mollie (otherwise 409 conflict). Requires the Online payments feature. Anyone with the link can view and pay the invoice, hence the invoices.send scope.
- Scope
-
invoices.send— Email invoices to customers and fetch the payment link for the customer - Required feature
invoices, invoice_payments
Path parameters
| Name | Type | Description |
|---|---|---|
invoice required |
string (uuid) | The id (UUID) of the invoice. |
Example request
curl "https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/payment-link" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/payment-link');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/payment-link', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/payment-link",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "invoice_payment_link",
"invoice_id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
"url": "https://app.klantly.com/invoice/Xk2p9Qm4Rt7vB1nC8dE5fG3hJ6kL0mN2pQ4rS7tU9vW1xY3zA5bC7dE9fG1hJ3kL/pay",
"amount_due": "1943.00",
"currency": "EUR"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
409
invalid_state_transition— This action is not possible in the current status. -
409
conflict— This conflicts with the current state.
Delete an invoice
/api/v1/invoices/{invoice}
Deletes the invoice. Only a draft can go: an invoice that has been sent stays.
- Scope
-
invoices.delete— Delete draft invoices - Required feature
invoices
Path parameters
| Name | Type | Description |
|---|---|---|
invoice required |
string (uuid) | The id (UUID) of the invoice. |
Example request
curl -X DELETE "https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('DELETE', 'invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.delete(
"https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "note",
"id": "9d3f7b41-2d6f-4e8c-9b3a-4f5d6e7a8b92",
"deleted": true
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
409
invalid_state_transition— This action is not possible in the current status.
The object
All fields are always present; a field without a value is null.
| Field | Type | Description |
|---|---|---|
object |
string | Always "invoice". |
id |
string (uuid) | Unique id (UUID). |
number |
string | Invoice number, for example FAC-00042; Klantly assigns it. |
type |
string | invoice or credit_note. A credit note has negative lines and totals. one of: invoice, credit_note |
status |
string | draft, sent, viewed, paid, partial (partly paid), overdue, cancelled, refunded or credited (fully reversed by credit notes; counts neither as paid nor as outstanding). one of: draft, sent, viewed, paid, partial, overdue, cancelled, refunded, credited |
title |
string | Title of the invoice. Required when creating. can be empty (null) |
description |
string | Short description; shown above the lines. can be empty (null) |
customer_id |
string (uuid) | The customer. Required when creating; name, address and contact details come from the customer. can be empty (null) |
customer |
object | The customer details on the invoice, as they were when it was created. |
customer.type |
string | individual or business. can be empty (null) |
customer.name |
string | Name; for a business the company name. can be empty (null) |
customer.email |
string | Email address. can be empty (null) |
customer.phone |
string | Phone number. can be empty (null) |
customer.address |
string | Street and house number. can be empty (null) |
customer.postal_code |
string | Postal code. can be empty (null) |
customer.city |
string | City. can be empty (null) |
customer.country |
string | Country. can be empty (null) |
customer.company_name |
string | Company name. can be empty (null) |
customer.vat_number |
string | VAT number. can be empty (null) |
customer.coc_number |
string | Chamber of commerce number. can be empty (null) |
quote_id |
string (uuid) | The quote this invoice came from, or null. can be empty (null) |
deal_id |
string (uuid) | The deal on the pipeline board this invoice belongs to, or null. can be empty (null) |
language |
string | Language of the invoice: nl, en, de or fr. one of: nl, en, de, fr |
currency |
string | Always "EUR". |
invoice_date |
string (date) | Invoice date (YYYY-MM-DD). can be empty (null) |
due_date |
string (date) | Due date (YYYY-MM-DD). Leave it out and it is the invoice date plus the payment term. can be empty (null) |
payment_term_days |
integer | Payment term in days. Without one, the company default applies. can be empty (null) |
is_term_invoice |
boolean | Whether this is a partial invoice for part of a quote. |
term_percentage |
string | Which share of the quote this partial invoice covers, as a percentage; otherwise null. can be empty (null) |
term_amount |
string | Fixed instalment amount including VAT (equal to the invoice total) when the instalment is set as an amount instead of a percentage; otherwise null. can be empty (null) |
terms_conditions |
string | Terms shown on the invoice. can be empty (null) |
notes |
string | Notes for the customer on the invoice. When recording a payment: a note about that payment. can be empty (null) |
outro_text |
string | Closing text below the lines. can be empty (null) |
hide_line_amounts |
boolean | Hides the per-line amounts on the invoice; the customer only sees the total. |
discount_percentage |
string | Discount on the total, as a percentage. |
discount_amount |
string | Discount on the total, as an amount. |
discount_description |
string | Why the discount was given; shown on the invoice. can be empty (null) |
subtotal |
string | Total excluding VAT, as a string with two decimals. |
tax_amount |
string | VAT amount. |
total |
string | Total including VAT. |
amount_paid |
string | What has been paid so far. |
amount_due |
string | What is still outstanding. After crediting a paid invoice this is clamped to 0.00; the amount to refund then sits on the credit note (refund_amount, credit_note_state). |
sent_at |
string (date-time) | When the invoice was sent to the customer. can be empty (null) |
viewed_at |
string (date-time) | When the customer opened it. can be empty (null) |
paid_at |
string (date-time) | When the invoice was settled in full. can be empty (null) |
cancelled_at |
string (date-time) | When the invoice was cancelled. can be empty (null) |
credited_invoice_id |
string (uuid) | On a credit note: the invoice it reverses; otherwise null. can be empty (null) |
credited_invoice_number |
string | On a credit note: the number of the credited invoice, recorded when it was created; otherwise null. can be empty (null) |
credit_reason |
string | On a credit note: the reason for the credit; otherwise null. can be empty (null) |
refund_amount |
string | On a credit note: the part that could not be offset against the open amount and has to be paid back to the customer (positive); null on an invoice. can be empty (null) |
refunded_at |
string (date-time) | On a credit note: when the refund was recorded; otherwise null. can be empty (null) |
credit_note_state |
string | On a credit note: refund_pending (money still has to go back), refunded or settled (fully offset against the invoice); null on an invoice. can be empty (null) · one of: refund_pending, refunded, settled |
credited_amount |
string | On an invoice: the total reversed by credit notes, including VAT (positive). amount_due is total minus paid minus credited. |
credit_note_ids |
array<string> | On an invoice: the ids of its credit notes, oldest first; an empty list on a credit note. |
items |
array<object> | The lines (up to 200). When sending: a list with name (required) per line and optionally type, description, sku, quantity, unit, unit_price, unit_price_incl, discount_percentage, discount_amount, discount_description, tax_rate and is_taxable. They replace all existing lines. |
items.object |
string | Always "invoice_item". |
items.id |
string (uuid) | Unique id of the line (UUID). |
items.type |
string | product, service, expense, discount or custom (a free amount on a credit note). one of: product, service, expense, discount, custom |
items.name |
string | Name of the line. |
items.description |
string | Description below the line. can be empty (null) |
items.sku |
string | Article number. can be empty (null) |
items.quantity |
string | Quantity. |
items.unit |
string | Unit, for example piece or hour. can be empty (null) |
items.unit_price |
string | Price per unit, excluding VAT. |
items.unit_price_incl |
string | Unit price including VAT, as entered. When set, the line is calculated from this amount (unit_price and line_total are derived from it), so the total matches the entered price to the cent. null = the line is calculated from unit_price. can be empty (null) |
items.discount_percentage |
string | Discount on this line, as a percentage. |
items.discount_amount |
string | Discount on this line, as an amount. |
items.discount_description |
string | Why the discount on this line was given. can be empty (null) |
items.line_total |
string | Line total excluding VAT, after discount. |
items.line_total_incl |
string | Line total including VAT, after discount. Only set when the line is calculated from unit_price_incl; otherwise null. can be empty (null) |
items.is_taxable |
boolean | Whether this line counts towards VAT. |
items.tax_rate |
string | VAT rate of this line, as a percentage. |
items.credited_item_id |
string (uuid) | On a credit note: the invoice line this line reverses; null for a free amount or on an invoice. can be empty (null) |
payments |
array<object> | The payments recorded on this invoice, oldest first. |
payments.object |
string | Always "invoice_payment". |
payments.id |
string (uuid) | Unique id of the payment. |
payments.amount |
string | The amount paid. |
payments.type |
string | full, deposit or partial. one of: full, deposit, partial |
payments.status |
string | open, pending, paid, failed, expired, cancelled or refunded. one of: open, pending, paid, failed, expired, cancelled, refunded |
payments.payment_method |
string | How it was paid, for example bank_transfer or ideal. can be empty (null) |
payments.description |
string | Note about the payment. can be empty (null) |
payments.paid_at |
string (date-time) | When the payment came in. can be empty (null) |
payments.created_at |
string (date-time) | When the payment was recorded. |
created_at |
string (date-time) | When the invoice was created. |
updated_at |
string (date-time) | When the invoice was last changed. |