Klantly Developers

API reference

Invoices

Invoices with their lines and payments: draft them, update them while they are still drafts, send them, cancel them and record payments.

Endpoints

List invoices

GET /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.

Scope
invoices.read — Read invoices, with their lines, amounts, payments and the customer details on them
Required feature
invoices

Query parameters

NameTypeDescription
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 Sort by created_at or updated_at; a leading minus sign sorts descending. one of: -created_at, created_at, -updated_at, updated_at · default: -created_at
filter[status] string Only invoices with this status, for example paid or overdue. one of: draft, sent, viewed, paid, partial, overdue, cancelled, refunded
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.

Example request

cURL
curl "https://app.klantly.com/api/v1/invoices?filter[status]=paid&sort=-updated_at" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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.

Example response
{
  "data": [
    {
      "object": "invoice",
      "id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
      "number": "FAC-00042",
      "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,
      "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,
      "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"
        }
      ],
      "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Get an invoice

GET /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

NameTypeDescription
invoice required string (uuid) The id (UUID) of the invoice.

Example request

cURL
curl "https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "data": {
    "object": "invoice",
    "id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
    "number": "FAC-00042",
    "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,
    "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,
    "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"
      }
    ],
    "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Create an invoice

POST /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 and update invoices (drafts only), cancel them and record payments
Required feature
invoices

Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.

Body (JSON)

FieldTypeDescription
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
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"
    }
  ]
}'
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "data": {
    "object": "invoice",
    "id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
    "number": "FAC-00042",
    "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,
    "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,
    "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"
      }
    ],
    "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Update an invoice

PATCH /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 and update invoices (drafts only), cancel them and record payments
Required feature
invoices

Send the ETag in If-Match and you will never accidentally overwrite a newer version.

Path parameters

NameTypeDescription
invoice required string (uuid) The id (UUID) of the invoice.

Body (JSON)

FieldTypeDescription
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
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."
}'
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "data": {
    "object": "invoice",
    "id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
    "number": "FAC-00042",
    "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,
    "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,
    "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"
      }
    ],
    "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Send an invoice

POST /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.

Scope
invoices.send — Email invoices to customers
Required feature
invoices

Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.

Path parameters

NameTypeDescription
invoice required string (uuid) The id (UUID) of the invoice.

Body (JSON)

FieldTypeDescription
message optional string Personal message in the email to the customer. can be empty (null) · at most 5000 characters

Example request

cURL
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."
}'
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "data": {
    "object": "invoice",
    "id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
    "number": "FAC-00042",
    "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,
    "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,
    "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"
      }
    ],
    "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Cancel an invoice

POST /api/v1/invoices/{invoice}/cancel

Sets the invoice to cancelled. A paid invoice cannot be cancelled; create a credit note for that.

Scope
invoices.write — Create and update invoices (drafts only), cancel them and record payments
Required feature
invoices

Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.

Path parameters

NameTypeDescription
invoice required string (uuid) The id (UUID) of the invoice.

Example request

cURL
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"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "data": {
    "object": "invoice",
    "id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
    "number": "FAC-00042",
    "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,
    "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,
    "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"
      }
    ],
    "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Record a payment

POST /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 and update invoices (drafts only), cancel them and record payments
Required feature
invoices

Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.

Path parameters

NameTypeDescription
invoice required string (uuid) The id (UUID) of the invoice.

Body (JSON)

FieldTypeDescription
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
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"
}'
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "data": {
    "object": "invoice",
    "id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dd",
    "number": "FAC-00042",
    "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,
    "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,
    "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"
      }
    ],
    "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Delete an invoice

DELETE /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

NameTypeDescription
invoice required string (uuid) The id (UUID) of the invoice.

Example request

cURL
curl -X DELETE "https://app.klantly.com/api/v1/invoices/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "data": {
    "object": "note",
    "id": "9d3f7b41-2d6f-4e8c-9b3a-4f5d6e7a8b92",
    "deleted": true
  }
}

Possible errors

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

The object

All fields are always present; a field without a value is null.

FieldTypeDescription
object string Always "invoice".
id string (uuid) Unique id (UUID).
number string Invoice number, for example FAC-00042; Klantly assigns it.
status string draft, sent, viewed, paid, partial (partly paid), overdue, cancelled or refunded. one of: draft, sent, viewed, paid, partial, overdue, cancelled, refunded
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)
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.
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)
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 or discount. one of: product, service, expense, discount
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.
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.