Klantly Developers

API-Referenz

Angebote

Angebote mit ihren Positionen: erstellen, ändern solange sie Entwurf sind, per E-Mail an den Kunden senden und das Ergebnis festhalten.

Endpunkte

Angebote abrufen

GET /api/v1/quotes

Eine Liste der Angebote, neueste zuerst, mit ihren Positionen. Filtern nach Status, Kunde oder Änderungsdatum. Mit filter[status]=sent und filter[updated_since] holen Sie ab, was seit Ihrer letzten Synchronisierung gesendet wurde.

Scope
quotes.read — Angebote lesen, mit Positionen, Beträgen und den Kundendaten darauf
Erforderliche Funktion
quotes

Query-Parameter

NameTypBeschreibung
limit integer Anzahl der Ergebnisse pro Seite. von 1 bis 100 · Standard: 50
cursor string Der next_cursor oder prev_cursor aus meta der vorherigen Antwort.
sort string Sortierung nach created_at oder updated_at; ein Minuszeichen davor sortiert absteigend. einer von: -created_at, created_at, -updated_at, updated_at · Standard: -created_at
filter[status] string Nur Angebote mit diesem Status, zum Beispiel sent oder accepted. einer von: draft, pending_review, sent, viewed, accepted, rejected, expired, completed
filter[customer_id] string (uuid) Nur was zu diesem Kunden gehört.
filter[updated_since] string (date-time) Nur was seit diesem Zeitpunkt geändert wurde: ISO 8601 mit Zeitzone, zum Beispiel 2026-09-14T10:15:00Z. Praktisch zum Synchronisieren.

Beispielanfrage

cURL
curl "https://app.klantly.com/api/v1/quotes?filter[status]=sent&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', 'quotes', [
    'query' => [
        'filter[status]' => 'sent',
        'sort' => '-updated_at',
    ],
]);

$data = json_decode((string) $response->getBody(), true)['data'];
JavaScript
const response = await fetch('https://app.klantly.com/api/v1/quotes?filter[status]=sent&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/quotes",
    headers={
        "Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
    },
    params={
        "filter[status]": "sent",
        "sort": "-updated_at"
    },
)
data = response.json()["data"]

Antwort 200

Die Antwort ist eine Liste mit Cursor-Paginierung: data enthält die Objekte, meta die Paginierung.

Beispielantwort
{
  "data": [
    {
      "object": "quote",
      "id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
      "number": "OFF-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
      },
      "deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
      "pipeline_stage_id": "stg_0q8v4n61h9x0zc",
      "language": "nl",
      "currency": "EUR",
      "quote_date": "2026-09-16",
      "valid_until": "2026-09-30",
      "intro_text": null,
      "outro_text": null,
      "terms_conditions": null,
      "notes": 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",
      "sent_at": null,
      "viewed_at": null,
      "accepted_at": null,
      "rejected_at": null,
      "invoice_id": null,
      "items": [
        {
          "object": "quote_item",
          "id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cc",
          "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"
        }
      ],
      "created_at": "2026-09-14T10:15:00Z",
      "updated_at": "2026-09-14T10:15:00Z"
    }
  ],
  "meta": {
    "limit": 50,
    "next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
    "prev_cursor": null
  }
}

Mögliche Fehler

Zusätzlich kann jeder Endpunkt die allgemeinen Fehler zurückgeben, etwa einen ungültigen Schlüssel oder ein erreichtes Limit. Alle Fehlercodes ansehen.

Angebot abrufen

GET /api/v1/quotes/{quote}

Ein Angebot per id, mit Positionen und Beträgen. Die Antwort enthält ein ETag, das Sie beim Ändern in If-Match mitsenden können.

Scope
quotes.read — Angebote lesen, mit Positionen, Beträgen und den Kundendaten darauf
Erforderliche Funktion
quotes

Pfadparameter

NameTypBeschreibung
quote erforderlich string (uuid) Die id (UUID) des Angebots.

Beispielanfrage

cURL
curl "https://app.klantly.com/api/v1/quotes/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', 'quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');

$data = json_decode((string) $response->getBody(), true)['data'];
JavaScript
const response = await fetch('https://app.klantly.com/api/v1/quotes/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/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
    headers={
        "Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
    },
)
data = response.json()["data"]

Antwort 200

Beispielantwort
{
  "data": {
    "object": "quote",
    "id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
    "number": "OFF-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
    },
    "deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
    "pipeline_stage_id": "stg_0q8v4n61h9x0zc",
    "language": "nl",
    "currency": "EUR",
    "quote_date": "2026-09-16",
    "valid_until": "2026-09-30",
    "intro_text": null,
    "outro_text": null,
    "terms_conditions": null,
    "notes": 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",
    "sent_at": null,
    "viewed_at": null,
    "accepted_at": null,
    "rejected_at": null,
    "invoice_id": null,
    "items": [
      {
        "object": "quote_item",
        "id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cc",
        "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"
      }
    ],
    "created_at": "2026-09-14T10:15:00Z",
    "updated_at": "2026-09-14T10:15:00Z"
  }
}

Mögliche Fehler

Zusätzlich kann jeder Endpunkt die allgemeinen Fehler zurückgeben, etwa einen ungültigen Schlüssel oder ein erreichtes Limit. Alle Fehlercodes ansehen.

Angebot erstellen

POST /api/v1/quotes

Erstellt ein Angebot für einen Kunden, immer als Entwurf. Klantly vergibt die Nummer und berechnet die Summen; Name, Adresse und Kontaktdaten kommen vom Kunden. Senden Sie es danach mit Angebot senden.

Scope
quotes.write — Angebote erstellen und bearbeiten (nur Entwürfe) sowie im Namen des Kunden annehmen oder ablehnen
Erforderliche Funktion
quotes

Senden Sie einen Idempotency-Key mit, dann erzeugt ein erneuter Versuch nach einem Timeout keinen doppelten Datensatz.

Body (JSON)

FeldTypBeschreibung
customer_id erforderlich string (uuid) Der Kunde. Beim Erstellen erforderlich; Name, Adresse und Kontaktdaten kommen vom Kunden.
title erforderlich string Titel des Angebots. Beim Erstellen erforderlich. höchstens 255 Zeichen
items erforderlich array Die Positionen (maximal 200). Beim Senden: eine Liste mit je Position name (erforderlich) und optional type, description, sku, quantity, unit, unit_price, unit_price_incl, discount_percentage, discount_amount, discount_description, tax_rate und is_taxable. Sie ersetzen alle bestehenden Positionen.
description optional string Kurze Beschreibung; steht über den Positionen. kann leer sein (null) · höchstens 20000 Zeichen
language optional string Sprache des Angebots: nl, en, de oder fr. einer von: nl, en, de, fr
quote_date optional string (date) Angebotsdatum (JJJJ-MM-TT).
valid_until optional string (date) Gültig bis einschließlich (JJJJ-MM-TT). Ohne Angabe gilt die Standardfrist des Unternehmens.
intro_text optional string Einleitung über den Positionen. kann leer sein (null) · höchstens 20000 Zeichen
outro_text optional string Schlusstext unter den Positionen. kann leer sein (null) · höchstens 20000 Zeichen
terms_conditions optional string Bedingungen auf dem Angebot. kann leer sein (null) · höchstens 20000 Zeichen
notes optional string Anmerkungen für den Kunden. kann leer sein (null) · höchstens 20000 Zeichen
hide_line_amounts optional boolean Blendet die Beträge je Position aus; der Kunde sieht nur die Summe.
discount_percentage optional number Rabatt auf die Summe, in Prozent. von 0 bis 100
discount_amount optional number Rabatt auf die Summe, als Betrag. von 0 bis 9999999
discount_description optional string Warum der Rabatt gewährt wurde; steht auf dem Angebot. kann leer sein (null) · höchstens 500 Zeichen

Beispielanfrage

cURL
curl -X POST "https://app.klantly.com/api/v1/quotes" \
  -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', 'quotes', [
    '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/quotes', {
  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/quotes",
    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"]

Antwort 201

Beispielantwort
{
  "data": {
    "object": "quote",
    "id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
    "number": "OFF-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
    },
    "deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
    "pipeline_stage_id": "stg_0q8v4n61h9x0zc",
    "language": "nl",
    "currency": "EUR",
    "quote_date": "2026-09-16",
    "valid_until": "2026-09-30",
    "intro_text": null,
    "outro_text": null,
    "terms_conditions": null,
    "notes": 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",
    "sent_at": null,
    "viewed_at": null,
    "accepted_at": null,
    "rejected_at": null,
    "invoice_id": null,
    "items": [
      {
        "object": "quote_item",
        "id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cc",
        "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"
      }
    ],
    "created_at": "2026-09-14T10:15:00Z",
    "updated_at": "2026-09-14T10:15:00Z"
  }
}

Mögliche Fehler

Zusätzlich kann jeder Endpunkt die allgemeinen Fehler zurückgeben, etwa einen ungültigen Schlüssel oder ein erreichtes Limit. Alle Fehlercodes ansehen.

Angebot ändern

PATCH /api/v1/quotes/{quote}

Ändert nur die Felder, die Sie mitsenden. items ersetzt alle Positionen als Ganzes; diese erhalten dabei neue ids. Nur ein Entwurf kann sich noch ändern: Ist das Angebot gesendet, hat der Kunde es gesehen, und Sie erhalten 409.

Scope
quotes.write — Angebote erstellen und bearbeiten (nur Entwürfe) sowie im Namen des Kunden annehmen oder ablehnen
Erforderliche Funktion
quotes

Senden Sie das ETag in If-Match mit, dann überschreiben Sie nie versehentlich eine neuere Version.

Pfadparameter

NameTypBeschreibung
quote erforderlich string (uuid) Die id (UUID) des Angebots.

Body (JSON)

FeldTypBeschreibung
title optional string Titel des Angebots. Beim Erstellen erforderlich. höchstens 255 Zeichen
description optional string Kurze Beschreibung; steht über den Positionen. kann leer sein (null) · höchstens 20000 Zeichen
language optional string Sprache des Angebots: nl, en, de oder fr. einer von: nl, en, de, fr
quote_date optional string (date) Angebotsdatum (JJJJ-MM-TT).
valid_until optional string (date) Gültig bis einschließlich (JJJJ-MM-TT). Ohne Angabe gilt die Standardfrist des Unternehmens.
intro_text optional string Einleitung über den Positionen. kann leer sein (null) · höchstens 20000 Zeichen
outro_text optional string Schlusstext unter den Positionen. kann leer sein (null) · höchstens 20000 Zeichen
terms_conditions optional string Bedingungen auf dem Angebot. kann leer sein (null) · höchstens 20000 Zeichen
notes optional string Anmerkungen für den Kunden. kann leer sein (null) · höchstens 20000 Zeichen
hide_line_amounts optional boolean Blendet die Beträge je Position aus; der Kunde sieht nur die Summe.
discount_percentage optional number Rabatt auf die Summe, in Prozent. von 0 bis 100
discount_amount optional number Rabatt auf die Summe, als Betrag. von 0 bis 9999999
discount_description optional string Warum der Rabatt gewährt wurde; steht auf dem Angebot. kann leer sein (null) · höchstens 500 Zeichen
items optional array Die Positionen (maximal 200). Beim Senden: eine Liste mit je Position name (erforderlich) und optional type, description, sku, quantity, unit, unit_price, unit_price_incl, discount_percentage, discount_amount, discount_description, tax_rate und is_taxable. Sie ersetzen alle bestehenden Positionen.

Beispielanfrage

cURL
curl -X PATCH "https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
  -H "Authorization: Bearer $KLANTLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "Veranda 400 × 300 (herzien)"
}'
PHP
$client = new \GuzzleHttp\Client([
    'base_uri' => 'https://app.klantly.com/api/v1/',
    'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);

$response = $client->request('PATCH', 'quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', [
    'json' => [
        'title' => 'Veranda 400 × 300 (herzien)',
    ],
]);

$data = json_decode((string) $response->getBody(), true)['data'];
JavaScript
const response = await fetch('https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
  "title": "Veranda 400 × 300 (herzien)"
}),
});

const { data } = await response.json();
Python
import os

import requests

response = requests.patch(
    "https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
    headers={
        "Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
    },
    json={
        "title": "Veranda 400 × 300 (herzien)"
    },
)
data = response.json()["data"]

Antwort 200

Beispielantwort
{
  "data": {
    "object": "quote",
    "id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
    "number": "OFF-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
    },
    "deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
    "pipeline_stage_id": "stg_0q8v4n61h9x0zc",
    "language": "nl",
    "currency": "EUR",
    "quote_date": "2026-09-16",
    "valid_until": "2026-09-30",
    "intro_text": null,
    "outro_text": null,
    "terms_conditions": null,
    "notes": 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",
    "sent_at": null,
    "viewed_at": null,
    "accepted_at": null,
    "rejected_at": null,
    "invoice_id": null,
    "items": [
      {
        "object": "quote_item",
        "id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cc",
        "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"
      }
    ],
    "created_at": "2026-09-14T10:15:00Z",
    "updated_at": "2026-09-14T10:15:00Z"
  }
}

Mögliche Fehler

Zusätzlich kann jeder Endpunkt die allgemeinen Fehler zurückgeben, etwa einen ungültigen Schlüssel oder ein erreichtes Limit. Alle Fehlercodes ansehen.

Angebot senden

POST /api/v1/quotes/{quote}/send

Sendet das Angebot per E-Mail an den Kunden über die Benachrichtigungsvorlage des Unternehmens, setzt den Status auf gesendet und hält den Zeitpunkt fest. Ihre eigene Nachricht erscheint in der E-Mail. Erneutes Senden nach einer Ablehnung setzt das Angebot wieder auf gesendet.

Scope
quotes.send — Angebote per E-Mail an Kunden senden
Erforderliche Funktion
quotes

Senden Sie einen Idempotency-Key mit, dann erzeugt ein erneuter Versuch nach einem Timeout keinen doppelten Datensatz.

Pfadparameter

NameTypBeschreibung
quote erforderlich string (uuid) Die id (UUID) des Angebots.

Body (JSON)

FeldTypBeschreibung
message optional string Persönliche Nachricht in der E-Mail an den Kunden. kann leer sein (null) · höchstens 5000 Zeichen

Beispielanfrage

cURL
curl -X POST "https://app.klantly.com/api/v1/quotes/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 offerte die we vanmorgen bespraken."
}'
PHP
$client = new \GuzzleHttp\Client([
    'base_uri' => 'https://app.klantly.com/api/v1/',
    'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);

$response = $client->request('POST', 'quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/send', [
    'headers' => [
        'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
    ],
    'json' => [
        'message' => 'Bijgaand de offerte die we vanmorgen bespraken.',
    ],
]);

$data = json_decode((string) $response->getBody(), true)['data'];
JavaScript
const response = await fetch('https://app.klantly.com/api/v1/quotes/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 offerte die we vanmorgen bespraken."
}),
});

const { data } = await response.json();
Python
import os

import requests

response = requests.post(
    "https://app.klantly.com/api/v1/quotes/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 offerte die we vanmorgen bespraken."
    },
)
data = response.json()["data"]

Antwort 200

Beispielantwort
{
  "data": {
    "object": "quote",
    "id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
    "number": "OFF-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
    },
    "deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
    "pipeline_stage_id": "stg_0q8v4n61h9x0zc",
    "language": "nl",
    "currency": "EUR",
    "quote_date": "2026-09-16",
    "valid_until": "2026-09-30",
    "intro_text": null,
    "outro_text": null,
    "terms_conditions": null,
    "notes": 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",
    "sent_at": null,
    "viewed_at": null,
    "accepted_at": null,
    "rejected_at": null,
    "invoice_id": null,
    "items": [
      {
        "object": "quote_item",
        "id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cc",
        "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"
      }
    ],
    "created_at": "2026-09-14T10:15:00Z",
    "updated_at": "2026-09-14T10:15:00Z"
  }
}

Mögliche Fehler

Zusätzlich kann jeder Endpunkt die allgemeinen Fehler zurückgeben, etwa einen ungültigen Schlüssel oder ein erreichtes Limit. Alle Fehlercodes ansehen.

Angebot annehmen

POST /api/v1/quotes/{quote}/accept

Hält fest, dass der Kunde zugestimmt hat, zum Beispiel nach einer Zusage außerhalb von Klantly. Ein angenommenes Angebot kann danach nicht mehr abgelehnt werden.

Scope
quotes.write — Angebote erstellen und bearbeiten (nur Entwürfe) sowie im Namen des Kunden annehmen oder ablehnen
Erforderliche Funktion
quotes

Senden Sie einen Idempotency-Key mit, dann erzeugt ein erneuter Versuch nach einem Timeout keinen doppelten Datensatz.

Pfadparameter

NameTypBeschreibung
quote erforderlich string (uuid) Die id (UUID) des Angebots.

Beispielanfrage

cURL
curl -X POST "https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/accept" \
  -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', 'quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/accept', [
    '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/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/accept', {
  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/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/accept",
    headers={
        "Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
        "Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
    },
)
data = response.json()["data"]

Antwort 200

Beispielantwort
{
  "data": {
    "object": "quote",
    "id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
    "number": "OFF-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
    },
    "deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
    "pipeline_stage_id": "stg_0q8v4n61h9x0zc",
    "language": "nl",
    "currency": "EUR",
    "quote_date": "2026-09-16",
    "valid_until": "2026-09-30",
    "intro_text": null,
    "outro_text": null,
    "terms_conditions": null,
    "notes": 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",
    "sent_at": null,
    "viewed_at": null,
    "accepted_at": null,
    "rejected_at": null,
    "invoice_id": null,
    "items": [
      {
        "object": "quote_item",
        "id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cc",
        "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"
      }
    ],
    "created_at": "2026-09-14T10:15:00Z",
    "updated_at": "2026-09-14T10:15:00Z"
  }
}

Mögliche Fehler

Zusätzlich kann jeder Endpunkt die allgemeinen Fehler zurückgeben, etwa einen ungültigen Schlüssel oder ein erreichtes Limit. Alle Fehlercodes ansehen.

Angebot ablehnen

POST /api/v1/quotes/{quote}/reject

Hält fest, dass der Kunde abgelehnt hat. Sie können danach ein überarbeitetes Angebot senden; das setzt es wieder auf gesendet.

Scope
quotes.write — Angebote erstellen und bearbeiten (nur Entwürfe) sowie im Namen des Kunden annehmen oder ablehnen
Erforderliche Funktion
quotes

Senden Sie einen Idempotency-Key mit, dann erzeugt ein erneuter Versuch nach einem Timeout keinen doppelten Datensatz.

Pfadparameter

NameTypBeschreibung
quote erforderlich string (uuid) Die id (UUID) des Angebots.

Beispielanfrage

cURL
curl -X POST "https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/reject" \
  -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', 'quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/reject', [
    '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/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/reject', {
  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/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/reject",
    headers={
        "Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
        "Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
    },
)
data = response.json()["data"]

Antwort 200

Beispielantwort
{
  "data": {
    "object": "quote",
    "id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
    "number": "OFF-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
    },
    "deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
    "pipeline_stage_id": "stg_0q8v4n61h9x0zc",
    "language": "nl",
    "currency": "EUR",
    "quote_date": "2026-09-16",
    "valid_until": "2026-09-30",
    "intro_text": null,
    "outro_text": null,
    "terms_conditions": null,
    "notes": 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",
    "sent_at": null,
    "viewed_at": null,
    "accepted_at": null,
    "rejected_at": null,
    "invoice_id": null,
    "items": [
      {
        "object": "quote_item",
        "id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cc",
        "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"
      }
    ],
    "created_at": "2026-09-14T10:15:00Z",
    "updated_at": "2026-09-14T10:15:00Z"
  }
}

Mögliche Fehler

Zusätzlich kann jeder Endpunkt die allgemeinen Fehler zurückgeben, etwa einen ungültigen Schlüssel oder ein erreichtes Limit. Alle Fehlercodes ansehen.

Angebot löschen

DELETE /api/v1/quotes/{quote}

Löscht das Angebot. Nur ein Entwurf kann weg: Ein gesendetes Angebot bleibt bestehen.

Scope
quotes.delete — Angebote löschen
Erforderliche Funktion
quotes

Pfadparameter

NameTypBeschreibung
quote erforderlich string (uuid) Die id (UUID) des Angebots.

Beispielanfrage

cURL
curl -X DELETE "https://app.klantly.com/api/v1/quotes/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', 'quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');

$data = json_decode((string) $response->getBody(), true)['data'];
JavaScript
const response = await fetch('https://app.klantly.com/api/v1/quotes/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/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
    headers={
        "Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
    },
)
data = response.json()["data"]

Antwort 200

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

Mögliche Fehler

Zusätzlich kann jeder Endpunkt die allgemeinen Fehler zurückgeben, etwa einen ungültigen Schlüssel oder ein erreichtes Limit. Alle Fehlercodes ansehen.

Das Objekt

Alle Felder sind immer vorhanden; ein Feld ohne Wert ist null.

FeldTypBeschreibung
object string Immer "quote".
id string (uuid) Eindeutige id (UUID).
number string Angebotsnummer, zum Beispiel OFF-00042; vergibt Klantly selbst.
status string draft (Entwurf), pending_review (zur Prüfung), sent (gesendet), viewed (angesehen), accepted (angenommen), rejected (abgelehnt), expired (abgelaufen) oder completed (abgeschlossen). einer von: draft, pending_review, sent, viewed, accepted, rejected, expired, completed
title string Titel des Angebots. Beim Erstellen erforderlich. kann leer sein (null)
description string Kurze Beschreibung; steht über den Positionen. kann leer sein (null)
customer_id string (uuid) Der Kunde. Beim Erstellen erforderlich; Name, Adresse und Kontaktdaten kommen vom Kunden. kann leer sein (null)
customer object Die Kundendaten auf dem Angebot, wie sie beim Erstellen waren.
customer.type string individual (Privatperson) oder business (Unternehmen). kann leer sein (null)
customer.name string Name; bei einem Unternehmen der Firmenname. kann leer sein (null)
customer.email string E-Mail-Adresse. kann leer sein (null)
customer.phone string Telefonnummer. kann leer sein (null)
customer.address string Straße und Hausnummer. kann leer sein (null)
customer.postal_code string Postleitzahl. kann leer sein (null)
customer.city string Ort. kann leer sein (null)
customer.country string Land. kann leer sein (null)
customer.company_name string Firmenname. kann leer sein (null)
customer.vat_number string USt-IdNr. kann leer sein (null)
customer.coc_number string Handelsregisternummer. kann leer sein (null)
deal_id string (uuid) Der Deal auf dem Pipeline-Board, zu dem dieses Angebot gehört, oder null. kann leer sein (null)
pipeline_stage_id string Die Phase, in der das Angebot steht, oder null. kann leer sein (null)
language string Sprache des Angebots: nl, en, de oder fr. einer von: nl, en, de, fr
currency string Immer "EUR".
quote_date string (date) Angebotsdatum (JJJJ-MM-TT). kann leer sein (null)
valid_until string (date) Gültig bis einschließlich (JJJJ-MM-TT). Ohne Angabe gilt die Standardfrist des Unternehmens. kann leer sein (null)
intro_text string Einleitung über den Positionen. kann leer sein (null)
outro_text string Schlusstext unter den Positionen. kann leer sein (null)
terms_conditions string Bedingungen auf dem Angebot. kann leer sein (null)
notes string Anmerkungen für den Kunden. kann leer sein (null)
hide_line_amounts boolean Blendet die Beträge je Position aus; der Kunde sieht nur die Summe.
discount_percentage string Rabatt auf die Summe, in Prozent.
discount_amount string Rabatt auf die Summe, als Betrag.
discount_description string Warum der Rabatt gewährt wurde; steht auf dem Angebot. kann leer sein (null)
subtotal string Summe ohne MwSt., als Text mit zwei Dezimalstellen.
tax_amount string MwSt.-Betrag.
total string Summe inklusive MwSt.
sent_at string (date-time) Wann das Angebot an den Kunden gesendet wurde. kann leer sein (null)
viewed_at string (date-time) Wann der Kunde es geöffnet hat. kann leer sein (null)
accepted_at string (date-time) Wann der Kunde zugestimmt hat. kann leer sein (null)
rejected_at string (date-time) Wann der Kunde abgelehnt hat. kann leer sein (null)
invoice_id string (uuid) Die aus diesem Angebot erstellte Rechnung, oder null. kann leer sein (null)
items array<object> Die Positionen (maximal 200). Beim Senden: eine Liste mit je Position name (erforderlich) und optional type, description, sku, quantity, unit, unit_price, unit_price_incl, discount_percentage, discount_amount, discount_description, tax_rate und is_taxable. Sie ersetzen alle bestehenden Positionen.
items.object string Immer "quote_item".
items.id string (uuid) Eindeutige id der Position (UUID).
items.type string product, service, discount oder fee. einer von: product, service, discount, fee
items.name string Name der Position.
items.description string Beschreibung unter der Position. kann leer sein (null)
items.sku string Artikelnummer. kann leer sein (null)
items.quantity string Menge.
items.unit string Einheit, zum Beispiel Stück oder Stunde. kann leer sein (null)
items.unit_price string Preis je Einheit, ohne MwSt.
items.unit_price_incl string Preis pro Einheit inklusive MwSt., wie eingegeben. Ist er gesetzt, wird die Position von diesem Betrag aus berechnet (unit_price und line_total werden daraus abgeleitet), und die Summe stimmt auf den Cent mit dem eingegebenen Preis überein. null = die Position wird von unit_price aus berechnet. kann leer sein (null)
items.discount_percentage string Rabatt auf diese Position, in Prozent.
items.discount_amount string Rabatt auf diese Position, als Betrag.
items.discount_description string Warum der Rabatt auf diese Position gewährt wurde. kann leer sein (null)
items.line_total string Positionssumme ohne MwSt., nach Rabatt.
items.line_total_incl string Positionssumme inklusive MwSt., nach Rabatt. Nur gesetzt, wenn die Position von unit_price_incl aus berechnet wird; sonst null. kann leer sein (null)
items.is_taxable boolean Ob diese Position für die MwSt. zählt.
items.tax_rate string MwSt.-Satz dieser Position, in Prozent.
created_at string (date-time) Wann das Angebot erstellt wurde.
updated_at string (date-time) Wann das Angebot zuletzt geändert wurde.