Klantly Developers

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

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

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 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
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",
      "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

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",
    "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

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 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)

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",
    "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

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

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",
    "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

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

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",
    "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

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

Mark as sent (no e-mail)

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

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/mark-sent" \
  -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/mark-sent', [
    '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/mark-sent', {
  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/mark-sent",
    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",
    "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

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

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",
    "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

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

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",
    "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

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

Credit an invoice

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

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

Body (JSON)

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

Example response
{
  "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

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

Record a refund

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

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

Body (JSON)

FieldTypeDescription
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
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"
}'
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/refunded', [
    'headers' => [
        'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
    ],
    'json' => [
        '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/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();
Python
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

Example response
{
  "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

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

Invoice as PDF

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

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/pdf" \
  -H "Authorization: Bearer $KLANTLY_API_KEY" \
  -o document.pdf
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/pdf');

file_put_contents('document.pdf', (string) $response->getBody());
JavaScript
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()));
Python
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

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