API-Referenz
Termine
Termine im Kalender: planen, verschieben, bestätigen, absagen und abschließen sowie dem Kunden eine Nachricht senden.
Endpunkte
-
GET
/appointmentsTermine auflisten -
GET
/appointments/{appointment}Termin abrufen -
POST
/appointmentsTermin planen -
PATCH
/appointments/{appointment}Termin bearbeiten -
POST
/appointments/{appointment}/confirmTermin bestätigen -
POST
/appointments/{appointment}/cancelTermin absagen -
POST
/appointments/{appointment}/completeTermin abschließen -
POST
/appointments/{appointment}/notifyNachricht an den Kunden senden -
DELETE
/appointments/{appointment}Termin löschen
Termine auflisten
/api/v1/appointments
Eine Liste von Terminen, neueste zuerst. Filtern Sie nach Status, Kunde, Benutzer, Beginn oder Änderungsdatum. Sortieren Sie nach starts_at für die Reihenfolge des Kalenders; Termine ohne Datum (eine Einladung) entfallen dann.
- Scope
-
appointments.read— Termine (mit Name, E-Mail und Telefon des Kunden), Terminarten und Verfügbarkeit lesen - Erforderliche Funktion
appointments
Query-Parameter
| Name | Typ | Beschreibung |
|---|---|---|
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, updated_at oder starts_at; ein Minuszeichen davor bedeutet absteigend. Bei starts_at entfallen Termine ohne Datum. einer von: -created_at, created_at, -updated_at, updated_at, -starts_at, starts_at · Standard: -created_at |
filter[status]
|
string | Nur Termine mit diesem Status: pending (noch nicht bestätigt), confirmed, cancelled oder completed. einer von: pending, confirmed, cancelled, completed |
filter[customer_id]
|
string (uuid) | Nur was zu diesem Kunden gehört. |
filter[user_id]
|
string | Nur Termine dieses Benutzers (die ID aus Benutzer auflisten). |
filter[starts_from]
|
string (date-time) | Nur Termine, die zu oder nach diesem Zeitpunkt beginnen: ISO 8601 mit Zeitzone. |
filter[starts_until]
|
string (date-time) | Nur Termine, die vor diesem Zeitpunkt beginnen: ISO 8601 mit Zeitzone. |
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 "https://app.klantly.com/api/v1/appointments?filter[starts_from]=2026-10-01T00%3A00%3A00Z&sort=starts_at" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'appointments', [
'query' => [
'filter[starts_from]' => '2026-10-01T00:00:00Z',
'sort' => 'starts_at',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/appointments?filter[starts_from]=2026-10-01T00%3A00%3A00Z&sort=starts_at', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/appointments",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
params={
"filter[starts_from]": "2026-10-01T00:00:00Z",
"sort": "starts_at"
},
)
data = response.json()["data"]Antwort 200
Die Antwort ist eine Liste mit Cursor-Paginierung: data enthält die Objekte, meta die Paginierung.
{
"data": [
{
"object": "appointment",
"id": "9d3f7d83-4f8b-4a0e-9d5c-6b7f8a9bacb4",
"title": "Inmeten veranda",
"description": null,
"status": "confirmed",
"starts_at": "2026-10-01T08:00:00Z",
"ends_at": "2026-10-01T09:00:00Z",
"all_day": false,
"location": "Dorpsstraat 1, Utrecht",
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"contact": {
"name": "Jan de Vries",
"email": "jan@example.com",
"phone": "+31 6 12345678"
},
"user_id": "usr_0k3j9x21m4zq8p",
"appointment_type_id": "9d3f7ea4-5a9c-4b1f-8e6d-7c8a9bacbdc5",
"deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"quote_id": null,
"invoice_id": null,
"notes": null,
"cancellation_reason": null,
"confirmed_at": "2026-09-14T10:15:00Z",
"cancelled_at": null,
"rescheduled_at": null,
"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
-
403
insufficient_scope— Dieser API-Schlüssel hat keinen Zugriff auf diese Aktion. -
422
validation_failed— Die Eingabe ist ungültig.
Termin abrufen
/api/v1/appointments/{appointment}
Ein Termin nach ID. Die Antwort enthält einen ETag, den Sie beim Bearbeiten in If-Match mitsenden können.
- Scope
-
appointments.read— Termine (mit Name, E-Mail und Telefon des Kunden), Terminarten und Verfügbarkeit lesen - Erforderliche Funktion
appointments
Pfadparameter
| Name | Typ | Beschreibung |
|---|---|---|
appointment erforderlich |
string (uuid) | Die ID (UUID) des Termins. |
Beispielanfrage
curl "https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Antwort 200
{
"data": {
"object": "appointment",
"id": "9d3f7d83-4f8b-4a0e-9d5c-6b7f8a9bacb4",
"title": "Inmeten veranda",
"description": null,
"status": "confirmed",
"starts_at": "2026-10-01T08:00:00Z",
"ends_at": "2026-10-01T09:00:00Z",
"all_day": false,
"location": "Dorpsstraat 1, Utrecht",
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"contact": {
"name": "Jan de Vries",
"email": "jan@example.com",
"phone": "+31 6 12345678"
},
"user_id": "usr_0k3j9x21m4zq8p",
"appointment_type_id": "9d3f7ea4-5a9c-4b1f-8e6d-7c8a9bacbdc5",
"deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"quote_id": null,
"invoice_id": null,
"notes": null,
"cancellation_reason": null,
"confirmed_at": "2026-09-14T10:15:00Z",
"cancelled_at": null,
"rescheduled_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Mögliche Fehler
-
403
insufficient_scope— Dieser API-Schlüssel hat keinen Zugriff auf diese Aktion. -
404
not_found— Nicht gefunden.
Termin planen
/api/v1/appointments
Plant einen Termin mit einem Kunden; Name, E-Mail und Telefon kommen vom Kunden. Ohne ends_at dauert der Termin so lange wie seine Terminart, sonst die Standarddauer aus den Termineinstellungen. Klantly prüft hier keine Verfügbarkeit: Ihre Planung ist maßgeblich. Die API selbst sendet dem Kunden keine E-Mail; dafür gibt es Nachricht an den Kunden senden. Hat das Unternehmen Automationen auf „Termin geplant“, laufen diese wie bei einem Termin im Kalender.
- Scope
-
appointments.write— Termine erstellen, bearbeiten, bestätigen, absagen und abschließen (Automationen des Unternehmens laufen mit) - Erforderliche Funktion
appointments
Senden Sie einen Idempotency-Key mit, dann erzeugt ein erneuter Versuch nach einem Timeout keinen doppelten Datensatz.
Body (JSON)
| Feld | Typ | Beschreibung |
|---|---|---|
customer_id
erforderlich
|
string (uuid) | Der Kunde des Termins. Beim Erstellen erforderlich. |
title
optional
|
string | Titel des Termins. kann leer sein (null) · höchstens 255 Zeichen · erforderlich ohne appointment_type_id |
description
optional
|
string | Beschreibung. kann leer sein (null) · höchstens 2000 Zeichen |
location
optional
|
string | Ort, zum Beispiel die Adresse des Kunden. kann leer sein (null) · höchstens 255 Zeichen |
starts_at
erforderlich
|
string (date-time) | Beginn (UTC). Leer bei einer Einladung, bei der der Kunde noch eine Zeit wählt. Bei der Eingabe: ISO 8601 mit Zeitzone. |
ends_at
optional
|
string (date-time) | Ende (UTC). Ohne ends_at beim Erstellen: die Dauer der Terminart oder die Standarddauer. kann leer sein (null) |
all_day
optional
|
boolean | Ein ganztägiger Termin. Zeiten stehen in der Antwort in UTC: Rechnen Sie für das Datum in die Zeitzone des Unternehmens (Europe/Amsterdam) zurück, sonst fällt ein Termin, der um 00:00 beginnt, auf den Vortag. |
appointment_type_id
optional
|
string (uuid) | Die Terminart, oder null. kann leer sein (null) |
user_id
optional
|
string | Der Benutzer, der den Termin hat, oder null. kann leer sein (null) |
status
optional
|
string | pending (noch nicht bestätigt), confirmed, cancelled (abgesagt) oder completed (abgeschlossen). Beim Erstellen pending oder confirmed (Standard). einer von: pending, confirmed |
notes
optional
|
string | Interne Notiz zum Termin. kann leer sein (null) · höchstens 2000 Zeichen |
Beispielanfrage
curl -X POST "https://app.klantly.com/api/v1/appointments" \
-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": "Inmeten veranda",
"starts_at": "2026-10-01T10:00:00+02:00",
"appointment_type_id": "9d3f7ea4-5a9c-4b1f-8e6d-7c8a9bacbdc5"
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'appointments', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'customer_id' => '9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70',
'title' => 'Inmeten veranda',
'starts_at' => '2026-10-01T10:00:00+02:00',
'appointment_type_id' => '9d3f7ea4-5a9c-4b1f-8e6d-7c8a9bacbdc5',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/appointments', {
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": "Inmeten veranda",
"starts_at": "2026-10-01T10:00:00+02:00",
"appointment_type_id": "9d3f7ea4-5a9c-4b1f-8e6d-7c8a9bacbdc5"
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/appointments",
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": "Inmeten veranda",
"starts_at": "2026-10-01T10:00:00+02:00",
"appointment_type_id": "9d3f7ea4-5a9c-4b1f-8e6d-7c8a9bacbdc5"
},
)
data = response.json()["data"]Antwort 201
{
"data": {
"object": "appointment",
"id": "9d3f7d83-4f8b-4a0e-9d5c-6b7f8a9bacb4",
"title": "Inmeten veranda",
"description": null,
"status": "confirmed",
"starts_at": "2026-10-01T08:00:00Z",
"ends_at": "2026-10-01T09:00:00Z",
"all_day": false,
"location": "Dorpsstraat 1, Utrecht",
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"contact": {
"name": "Jan de Vries",
"email": "jan@example.com",
"phone": "+31 6 12345678"
},
"user_id": "usr_0k3j9x21m4zq8p",
"appointment_type_id": "9d3f7ea4-5a9c-4b1f-8e6d-7c8a9bacbdc5",
"deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"quote_id": null,
"invoice_id": null,
"notes": null,
"cancellation_reason": null,
"confirmed_at": "2026-09-14T10:15:00Z",
"cancelled_at": null,
"rescheduled_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Mögliche Fehler
-
403
insufficient_scope— Dieser API-Schlüssel hat keinen Zugriff auf diese Aktion. -
422
validation_failed— Die Eingabe ist ungültig. -
422
unknown_field— Die Eingabe enthält ein unbekanntes Feld. -
415
unsupported_media_type— Dieses Format wird nicht unterstützt. -
413
payload_too_large— Der Body der Anfrage ist zu groß. -
422
idempotency_key_reused— Dieser Idempotency-Key wurde bereits für eine andere Anfrage verwendet. -
409
idempotency_in_progress— Eine Anfrage mit diesem Idempotency-Key wird noch verarbeitet.
Termin bearbeiten
/api/v1/appointments/{appointment}
Ändert nur die Felder, die Sie mitsenden. Ein neues starts_at ist eine Verschiebung: rescheduled_at wird gesetzt, und ohne ends_at bleibt die Dauer gleich. Erhält ein Termin ohne Datum ein starts_at, kommt ends_at wie beim Erstellen hinzu; ein ends_at ohne Startzeit ist nicht möglich (422). Den Status ändern Sie mit Bestätigen, Absagen oder Abschließen.
- Scope
-
appointments.write— Termine erstellen, bearbeiten, bestätigen, absagen und abschließen (Automationen des Unternehmens laufen mit) - Erforderliche Funktion
appointments
Senden Sie das ETag in If-Match mit, dann überschreiben Sie nie versehentlich eine neuere Version.
Pfadparameter
| Name | Typ | Beschreibung |
|---|---|---|
appointment erforderlich |
string (uuid) | Die ID (UUID) des Termins. |
Body (JSON)
| Feld | Typ | Beschreibung |
|---|---|---|
title
optional
|
string | Titel des Termins. höchstens 255 Zeichen |
description
optional
|
string | Beschreibung. kann leer sein (null) · höchstens 2000 Zeichen |
location
optional
|
string | Ort, zum Beispiel die Adresse des Kunden. kann leer sein (null) · höchstens 255 Zeichen |
starts_at
optional
|
string (date-time) | Beginn (UTC). Leer bei einer Einladung, bei der der Kunde noch eine Zeit wählt. Bei der Eingabe: ISO 8601 mit Zeitzone. |
ends_at
optional
|
string (date-time) | Ende (UTC). Ohne ends_at beim Erstellen: die Dauer der Terminart oder die Standarddauer. |
all_day
optional
|
boolean | Ein ganztägiger Termin. Zeiten stehen in der Antwort in UTC: Rechnen Sie für das Datum in die Zeitzone des Unternehmens (Europe/Amsterdam) zurück, sonst fällt ein Termin, der um 00:00 beginnt, auf den Vortag. |
appointment_type_id
optional
|
string (uuid) | Die Terminart, oder null. kann leer sein (null) |
user_id
optional
|
string | Der Benutzer, der den Termin hat, oder null. kann leer sein (null) |
notes
optional
|
string | Interne Notiz zum Termin. kann leer sein (null) · höchstens 2000 Zeichen |
Beispielanfrage
curl -X PATCH "https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"starts_at": "2026-10-02T09:00:00Z"
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('PATCH', 'appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', [
'json' => [
'starts_at' => '2026-10-02T09:00:00Z',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"starts_at": "2026-10-02T09:00:00Z"
}),
});
const { data } = await response.json();import os
import requests
response = requests.patch(
"https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
json={
"starts_at": "2026-10-02T09:00:00Z"
},
)
data = response.json()["data"]Antwort 200
{
"data": {
"object": "appointment",
"id": "9d3f7d83-4f8b-4a0e-9d5c-6b7f8a9bacb4",
"title": "Inmeten veranda",
"description": null,
"status": "confirmed",
"starts_at": "2026-10-01T08:00:00Z",
"ends_at": "2026-10-01T09:00:00Z",
"all_day": false,
"location": "Dorpsstraat 1, Utrecht",
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"contact": {
"name": "Jan de Vries",
"email": "jan@example.com",
"phone": "+31 6 12345678"
},
"user_id": "usr_0k3j9x21m4zq8p",
"appointment_type_id": "9d3f7ea4-5a9c-4b1f-8e6d-7c8a9bacbdc5",
"deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"quote_id": null,
"invoice_id": null,
"notes": null,
"cancellation_reason": null,
"confirmed_at": "2026-09-14T10:15:00Z",
"cancelled_at": null,
"rescheduled_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Mögliche Fehler
-
403
insufficient_scope— Dieser API-Schlüssel hat keinen Zugriff auf diese Aktion. -
422
validation_failed— Die Eingabe ist ungültig. -
422
unknown_field— Die Eingabe enthält ein unbekanntes Feld. -
415
unsupported_media_type— Dieses Format wird nicht unterstützt. -
413
payload_too_large— Der Body der Anfrage ist zu groß. -
404
not_found— Nicht gefunden. -
412
precondition_failed— Der Datensatz wurde inzwischen geändert.
Termin bestätigen
/api/v1/appointments/{appointment}/confirm
Setzt einen Termin auf bestätigt. Ist er bereits bestätigt, ändert sich nichts.
- Scope
-
appointments.write— Termine erstellen, bearbeiten, bestätigen, absagen und abschließen (Automationen des Unternehmens laufen mit) - Erforderliche Funktion
appointments
Senden Sie einen Idempotency-Key mit, dann erzeugt ein erneuter Versuch nach einem Timeout keinen doppelten Datensatz.
Pfadparameter
| Name | Typ | Beschreibung |
|---|---|---|
appointment erforderlich |
string (uuid) | Die ID (UUID) des Termins. |
Beispielanfrage
curl -X POST "https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/confirm" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/confirm', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/confirm', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/confirm",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Antwort 200
{
"data": {
"object": "appointment",
"id": "9d3f7d83-4f8b-4a0e-9d5c-6b7f8a9bacb4",
"title": "Inmeten veranda",
"description": null,
"status": "confirmed",
"starts_at": "2026-10-01T08:00:00Z",
"ends_at": "2026-10-01T09:00:00Z",
"all_day": false,
"location": "Dorpsstraat 1, Utrecht",
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"contact": {
"name": "Jan de Vries",
"email": "jan@example.com",
"phone": "+31 6 12345678"
},
"user_id": "usr_0k3j9x21m4zq8p",
"appointment_type_id": "9d3f7ea4-5a9c-4b1f-8e6d-7c8a9bacbdc5",
"deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"quote_id": null,
"invoice_id": null,
"notes": null,
"cancellation_reason": null,
"confirmed_at": "2026-09-14T10:15:00Z",
"cancelled_at": null,
"rescheduled_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Mögliche Fehler
-
403
insufficient_scope— Dieser API-Schlüssel hat keinen Zugriff auf diese Aktion. -
404
not_found— Nicht gefunden. -
409
invalid_state_transition— Diese Aktion ist im aktuellen Status nicht möglich. -
422
idempotency_key_reused— Dieser Idempotency-Key wurde bereits für eine andere Anfrage verwendet. -
409
idempotency_in_progress— Eine Anfrage mit diesem Idempotency-Key wird noch verarbeitet.
Termin absagen
/api/v1/appointments/{appointment}/cancel
Sagt den Termin ab, optional mit einem Grund. Ein abgeschlossener Termin kann nicht mehr abgesagt werden. Automationen auf „Termin abgesagt“ laufen wie im Kalender.
- Scope
-
appointments.write— Termine erstellen, bearbeiten, bestätigen, absagen und abschließen (Automationen des Unternehmens laufen mit) - Erforderliche Funktion
appointments
Senden Sie einen Idempotency-Key mit, dann erzeugt ein erneuter Versuch nach einem Timeout keinen doppelten Datensatz.
Pfadparameter
| Name | Typ | Beschreibung |
|---|---|---|
appointment erforderlich |
string (uuid) | Die ID (UUID) des Termins. |
Body (JSON)
| Feld | Typ | Beschreibung |
|---|---|---|
reason
optional
|
string | Der Grund der Absage (optional). kann leer sein (null) · höchstens 500 Zeichen |
Beispielanfrage
curl -X POST "https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/cancel" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f" \
-d '{
"reason": "Klant is verhinderd"
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/cancel', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'reason' => 'Klant is verhinderd',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/cancel', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
body: JSON.stringify({
"reason": "Klant is verhinderd"
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/cancel",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
json={
"reason": "Klant is verhinderd"
},
)
data = response.json()["data"]Antwort 200
{
"data": {
"object": "appointment",
"id": "9d3f7d83-4f8b-4a0e-9d5c-6b7f8a9bacb4",
"title": "Inmeten veranda",
"description": null,
"status": "confirmed",
"starts_at": "2026-10-01T08:00:00Z",
"ends_at": "2026-10-01T09:00:00Z",
"all_day": false,
"location": "Dorpsstraat 1, Utrecht",
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"contact": {
"name": "Jan de Vries",
"email": "jan@example.com",
"phone": "+31 6 12345678"
},
"user_id": "usr_0k3j9x21m4zq8p",
"appointment_type_id": "9d3f7ea4-5a9c-4b1f-8e6d-7c8a9bacbdc5",
"deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"quote_id": null,
"invoice_id": null,
"notes": null,
"cancellation_reason": null,
"confirmed_at": "2026-09-14T10:15:00Z",
"cancelled_at": null,
"rescheduled_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Mögliche Fehler
-
403
insufficient_scope— Dieser API-Schlüssel hat keinen Zugriff auf diese Aktion. -
422
validation_failed— Die Eingabe ist ungültig. -
422
unknown_field— Die Eingabe enthält ein unbekanntes Feld. -
415
unsupported_media_type— Dieses Format wird nicht unterstützt. -
413
payload_too_large— Der Body der Anfrage ist zu groß. -
404
not_found— Nicht gefunden. -
409
invalid_state_transition— Diese Aktion ist im aktuellen Status nicht möglich. -
422
idempotency_key_reused— Dieser Idempotency-Key wurde bereits für eine andere Anfrage verwendet. -
409
idempotency_in_progress— Eine Anfrage mit diesem Idempotency-Key wird noch verarbeitet.
Termin abschließen
/api/v1/appointments/{appointment}/complete
Schließt den Termin ab. Steht die Lead-Umwandlung des Unternehmens auf „Termin abgeschlossen“, wird ein Lead dabei zum Kunden, genau wie im Kalender; bei den anderen Einstellungen bleibt er Lead.
- Scope
-
appointments.write— Termine erstellen, bearbeiten, bestätigen, absagen und abschließen (Automationen des Unternehmens laufen mit) - Erforderliche Funktion
appointments
Senden Sie einen Idempotency-Key mit, dann erzeugt ein erneuter Versuch nach einem Timeout keinen doppelten Datensatz.
Pfadparameter
| Name | Typ | Beschreibung |
|---|---|---|
appointment erforderlich |
string (uuid) | Die ID (UUID) des Termins. |
Beispielanfrage
curl -X POST "https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/complete" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/complete', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/complete', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/complete",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Antwort 200
{
"data": {
"object": "appointment",
"id": "9d3f7d83-4f8b-4a0e-9d5c-6b7f8a9bacb4",
"title": "Inmeten veranda",
"description": null,
"status": "confirmed",
"starts_at": "2026-10-01T08:00:00Z",
"ends_at": "2026-10-01T09:00:00Z",
"all_day": false,
"location": "Dorpsstraat 1, Utrecht",
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"contact": {
"name": "Jan de Vries",
"email": "jan@example.com",
"phone": "+31 6 12345678"
},
"user_id": "usr_0k3j9x21m4zq8p",
"appointment_type_id": "9d3f7ea4-5a9c-4b1f-8e6d-7c8a9bacbdc5",
"deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"quote_id": null,
"invoice_id": null,
"notes": null,
"cancellation_reason": null,
"confirmed_at": "2026-09-14T10:15:00Z",
"cancelled_at": null,
"rescheduled_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Mögliche Fehler
-
403
insufficient_scope— Dieser API-Schlüssel hat keinen Zugriff auf diese Aktion. -
404
not_found— Nicht gefunden. -
409
invalid_state_transition— Diese Aktion ist im aktuellen Status nicht möglich. -
422
idempotency_key_reused— Dieser Idempotency-Key wurde bereits für eine andere Anfrage verwendet. -
409
idempotency_in_progress— Eine Anfrage mit diesem Idempotency-Key wird noch verarbeitet.
Nachricht an den Kunden senden
/api/v1/appointments/{appointment}/notify
Sendet dem Kunden per E-Mail eine Bestätigung, Verschiebung, Absage oder Erinnerung, mit der Vorlage, die das Unternehmen in Klantly eingerichtet hat. Die Nachricht muss zum Status des Termins passen. Hat das Unternehmen diese Vorlage ausgeschaltet, erhalten Sie 409 und es wird nichts gesendet. Erfordert den Scope appointments.send.
- Scope
-
appointments.send— Terminnachrichten per E-Mail an Kunden senden - Erforderliche Funktion
appointments
Senden Sie einen Idempotency-Key mit, dann erzeugt ein erneuter Versuch nach einem Timeout keinen doppelten Datensatz.
Pfadparameter
| Name | Typ | Beschreibung |
|---|---|---|
appointment erforderlich |
string (uuid) | Die ID (UUID) des Termins. |
Body (JSON)
| Feld | Typ | Beschreibung |
|---|---|---|
message
erforderlich
|
string | Welche Nachricht: confirmation (Bestätigung), reschedule (Verschiebung), cancellation (Absage) oder reminder (Erinnerung). einer von: confirmation, reschedule, cancellation, reminder |
Beispielanfrage
curl -X POST "https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/notify" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f" \
-d '{
"message": "confirmation"
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/notify', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'message' => 'confirmation',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/notify', {
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": "confirmation"
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/notify",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
json={
"message": "confirmation"
},
)
data = response.json()["data"]Antwort 200
{
"data": {
"object": "appointment",
"id": "9d3f7d83-4f8b-4a0e-9d5c-6b7f8a9bacb4",
"title": "Inmeten veranda",
"description": null,
"status": "confirmed",
"starts_at": "2026-10-01T08:00:00Z",
"ends_at": "2026-10-01T09:00:00Z",
"all_day": false,
"location": "Dorpsstraat 1, Utrecht",
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"contact": {
"name": "Jan de Vries",
"email": "jan@example.com",
"phone": "+31 6 12345678"
},
"user_id": "usr_0k3j9x21m4zq8p",
"appointment_type_id": "9d3f7ea4-5a9c-4b1f-8e6d-7c8a9bacbdc5",
"deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"quote_id": null,
"invoice_id": null,
"notes": null,
"cancellation_reason": null,
"confirmed_at": "2026-09-14T10:15:00Z",
"cancelled_at": null,
"rescheduled_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Mögliche Fehler
-
403
insufficient_scope— Dieser API-Schlüssel hat keinen Zugriff auf diese Aktion. -
422
validation_failed— Die Eingabe ist ungültig. -
422
unknown_field— Die Eingabe enthält ein unbekanntes Feld. -
415
unsupported_media_type— Dieses Format wird nicht unterstützt. -
413
payload_too_large— Der Body der Anfrage ist zu groß. -
404
not_found— Nicht gefunden. -
409
invalid_state_transition— Diese Aktion ist im aktuellen Status nicht möglich. -
422
idempotency_key_reused— Dieser Idempotency-Key wurde bereits für eine andere Anfrage verwendet. -
409
idempotency_in_progress— Eine Anfrage mit diesem Idempotency-Key wird noch verarbeitet.
Termin löschen
/api/v1/appointments/{appointment}
Löscht den Termin endgültig, auch aus dem verknüpften Google-Kalender. Soll er nur entfallen, sagen Sie ihn stattdessen ab.
- Scope
-
appointments.delete— Termine löschen - Erforderliche Funktion
appointments
Pfadparameter
| Name | Typ | Beschreibung |
|---|---|---|
appointment erforderlich |
string (uuid) | Die ID (UUID) des Termins. |
Beispielanfrage
curl -X DELETE "https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('DELETE', 'appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.delete(
"https://app.klantly.com/api/v1/appointments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Antwort 200
{
"data": {
"object": "note",
"id": "9d3f7b41-2d6f-4e8c-9b3a-4f5d6e7a8b92",
"deleted": true
}
}Mögliche Fehler
-
403
insufficient_scope— Dieser API-Schlüssel hat keinen Zugriff auf diese Aktion. -
404
not_found— Nicht gefunden.
Das Objekt
Alle Felder sind immer vorhanden; ein Feld ohne Wert ist null.
| Feld | Typ | Beschreibung |
|---|---|---|
object |
string | Immer „appointment“. |
id |
string (uuid) | Eindeutige ID (UUID). |
title |
string | Titel des Termins. |
description |
string | Beschreibung. kann leer sein (null) |
status |
string | pending (noch nicht bestätigt), confirmed, cancelled (abgesagt) oder completed (abgeschlossen). Beim Erstellen pending oder confirmed (Standard). einer von: pending, confirmed, cancelled, completed |
starts_at |
string (date-time) | Beginn (UTC). Leer bei einer Einladung, bei der der Kunde noch eine Zeit wählt. Bei der Eingabe: ISO 8601 mit Zeitzone. kann leer sein (null) |
ends_at |
string (date-time) | Ende (UTC). Ohne ends_at beim Erstellen: die Dauer der Terminart oder die Standarddauer. kann leer sein (null) |
all_day |
boolean | Ein ganztägiger Termin. Zeiten stehen in der Antwort in UTC: Rechnen Sie für das Datum in die Zeitzone des Unternehmens (Europe/Amsterdam) zurück, sonst fällt ein Termin, der um 00:00 beginnt, auf den Vortag. |
location |
string | Ort, zum Beispiel die Adresse des Kunden. kann leer sein (null) |
customer_id |
string (uuid) | Der Kunde des Termins. Beim Erstellen erforderlich. kann leer sein (null) |
contact |
object | Die Kontaktdaten, mit denen der Termin angelegt wurde. |
contact.name |
string | Name. kann leer sein (null) |
contact.email |
string | E-Mail-Adresse; hierhin gehen Nachrichten an den Kunden. kann leer sein (null) |
contact.phone |
string | Telefonnummer. kann leer sein (null) |
user_id |
string | Der Benutzer, der den Termin hat, oder null. kann leer sein (null) |
appointment_type_id |
string (uuid) | Die Terminart, oder null. kann leer sein (null) |
deal_id |
string (uuid) | Der Deal auf dem Pipeline-Board, zu dem der Termin gehört (verknüpft Klantly selbst). kann leer sein (null) |
quote_id |
string (uuid) | Das verknüpfte Angebot, oder null. kann leer sein (null) |
invoice_id |
string (uuid) | Die verknüpfte Rechnung, oder null. kann leer sein (null) |
notes |
string | Interne Notiz zum Termin. kann leer sein (null) |
cancellation_reason |
string | Warum der Termin abgesagt wurde, oder null. kann leer sein (null) |
confirmed_at |
string (date-time) | Wann der Termin bestätigt wurde. kann leer sein (null) |
cancelled_at |
string (date-time) | Wann der Termin abgesagt wurde. kann leer sein (null) |
rescheduled_at |
string (date-time) | Wann der Termin zuletzt verschoben wurde. kann leer sein (null) |
created_at |
string (date-time) | Erstellt am (UTC). |
updated_at |
string (date-time) | Zuletzt geändert am (UTC). |