API-Referenz
Arbeitsaufträge
Arbeitsaufträge mit Positionen und Checkliste: zum Beispiel aus einem ERP erstellen, bearbeiten, den Status ändern und abgeschlossene Aufträge abrufen.
Endpunkte
-
GET
/work-ordersArbeitsaufträge auflisten -
GET
/work-orders/{work_order}Arbeitsauftrag abrufen -
POST
/work-ordersArbeitsauftrag erstellen -
PATCH
/work-orders/{work_order}Arbeitsauftrag bearbeiten -
POST
/work-orders/{work_order}/statusStatus eines Arbeitsauftrags ändern -
DELETE
/work-orders/{work_order}Arbeitsauftrag löschen
Arbeitsaufträge auflisten
/api/v1/work-orders
Eine Liste von Arbeitsaufträgen, neueste zuerst, mit Positionen, Checkliste und Fotos. Filtern Sie nach Status, Kunde, Benutzer, Termin oder Änderungsdatum oder suchen Sie mit q in Nummer, Kundenname und Titel. Mit filter[status]=completed und filter[updated_since] holen Sie abgeschlossene Arbeit ab.
- Scope
-
work_orders.read— Arbeitsaufträge lesen, mit Name, Adresse und Kontaktdaten des Kunden - Erforderliche Funktion
work_orders
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. |
q
|
string | Sucht in Arbeitsauftragsnummer, Kundenname und Titel. mindestens 2 Zeichen · höchstens 100 Zeichen |
sort
|
string | Das Feld, nach dem sortiert wird; ein Minuszeichen davor bedeutet absteigend. Welche Felder Sie wählen können und was der Standard ist, steht bei diesem Parameter. einer von: -created_at, created_at, -updated_at, updated_at · Standard: -created_at |
filter[status]
|
string | Nur Arbeitsaufträge mit diesem Status. einer von: draft, planned, in_progress, completed, invoiced, cancelled |
filter[customer_id]
|
string (uuid) | Nur was zu diesem Kunden gehört. |
filter[assigned_user_id]
|
string | Nur was diesem Benutzer zugewiesen ist (die ID aus Benutzer auflisten). |
filter[appointment_id]
|
string (uuid) | Nur Arbeitsaufträge zu diesem Termin. |
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. Überlappen Sie Ihren vorherigen Zeitpunkt um einige Minuten, weil die Uhr Ihres Systems und die von Klantly nicht exakt gleich laufen und weil eine Änderung kurz nach Ihrem vorherigen Zeitpunkt sonst knapp außerhalb liegen würde. Die Grenze ist inklusiv: etwas, das genau zu diesem Zeitpunkt geändert wurde, zählt mit. |
Beispielanfrage
curl "https://app.klantly.com/api/v1/work-orders?filter[status]=completed&sort=-updated_at" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'work-orders', [
'query' => [
'filter[status]' => 'completed',
'sort' => '-updated_at',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/work-orders?filter[status]=completed&sort=-updated_at', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/work-orders",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
params={
"filter[status]": "completed",
"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.
{
"data": [
{
"object": "work_order",
"id": "9d3f8328-9ed0-4f5d-8cab-b0cedfe0f1a9",
"number": "WB-2026-00042",
"status": "planned",
"title": "Onderhoud cv-ketel",
"type": "onderhoud",
"location": null,
"description": null,
"work_performed": null,
"customer_notes": 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"
},
"deal_id": null,
"appointment_id": null,
"quote_id": null,
"invoice_id": null,
"assigned_user_id": "usr_0k3j9x21m4zq8p",
"language": "nl",
"currency": "EUR",
"subtotal": "90.00",
"tax_amount": "18.90",
"total": "108.90",
"scheduled_at": "2026-10-01T08:00:00Z",
"started_at": null,
"completed_at": null,
"sent_at": null,
"signature": {
"is_signed": false,
"signed_by_name": null,
"signed_at": null
},
"items": [
{
"object": "work_order_item",
"id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cb",
"type": "labor",
"name": "Arbeid",
"description": null,
"sku": null,
"quantity": "2.00",
"unit": "uur",
"unit_price": "45.00",
"discount_percentage": "0.00",
"discount_amount": "0.00",
"line_total": "90.00",
"is_taxable": true,
"tax_rate": "21.00",
"minutes": 120
}
],
"checklist": [
{
"object": "work_order_checklist_item",
"id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dc",
"label": "Druk gecontroleerd",
"checked": false,
"required": true,
"note": null
}
],
"photos": [],
"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.
Arbeitsauftrag abrufen
/api/v1/work-orders/{work_order}
Ein Arbeitsauftrag nach ID, mit Positionen, Checkliste, Fotos (mit Download-Link) und dem Stand der Unterschrift.
- Scope
-
work_orders.read— Arbeitsaufträge lesen, mit Name, Adresse und Kontaktdaten des Kunden - Erforderliche Funktion
work_orders
Pfadparameter
| Name | Typ | Beschreibung |
|---|---|---|
work_order erforderlich |
string (uuid) | Die ID (UUID) des Arbeitsauftrags. |
Beispielanfrage
curl "https://app.klantly.com/api/v1/work-orders/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', 'work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/work-orders/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/work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Antwort 200
{
"data": {
"object": "work_order",
"id": "9d3f8328-9ed0-4f5d-8cab-b0cedfe0f1a9",
"number": "WB-2026-00042",
"status": "planned",
"title": "Onderhoud cv-ketel",
"type": "onderhoud",
"location": null,
"description": null,
"work_performed": null,
"customer_notes": 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"
},
"deal_id": null,
"appointment_id": null,
"quote_id": null,
"invoice_id": null,
"assigned_user_id": "usr_0k3j9x21m4zq8p",
"language": "nl",
"currency": "EUR",
"subtotal": "90.00",
"tax_amount": "18.90",
"total": "108.90",
"scheduled_at": "2026-10-01T08:00:00Z",
"started_at": null,
"completed_at": null,
"sent_at": null,
"signature": {
"is_signed": false,
"signed_by_name": null,
"signed_at": null
},
"items": [
{
"object": "work_order_item",
"id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cb",
"type": "labor",
"name": "Arbeid",
"description": null,
"sku": null,
"quantity": "2.00",
"unit": "uur",
"unit_price": "45.00",
"discount_percentage": "0.00",
"discount_amount": "0.00",
"line_total": "90.00",
"is_taxable": true,
"tax_rate": "21.00",
"minutes": 120
}
],
"checklist": [
{
"object": "work_order_checklist_item",
"id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dc",
"label": "Druk gecontroleerd",
"checked": false,
"required": true,
"note": null
}
],
"photos": [],
"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.
Arbeitsauftrag erstellen
/api/v1/work-orders
Erstellt einen Arbeitsauftrag für einen Kunden, als Entwurf oder geplant. Klantly vergibt eine Nummer und berechnet die Summen; Name, Adresse und Kontaktdaten kommen vom Kunden.
- Scope
-
work_orders.write— Arbeitsaufträge erstellen und bearbeiten sowie den Status ändern (Abschließen kann eine Bewertungsanfrage senden) - Erforderliche Funktion
work_orders
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. Beim Erstellen erforderlich; Name, Adresse und Kontaktdaten kommen vom Kunden. |
status
optional
|
string | draft (Entwurf), planned (geplant), in_progress (in Arbeit), completed (abgeschlossen), invoiced (abgerechnet) oder cancelled (storniert). Beim Erstellen draft oder planned. einer von: draft, planned |
title
optional
|
string | Titel. kann leer sein (null) · höchstens 255 Zeichen |
type
optional
|
string | Art der Arbeit, zum Beispiel Installation, Reparatur oder Wartung. kann leer sein (null) · höchstens 64 Zeichen |
location
optional
|
string | Wo die Arbeit stattfindet, wenn nicht an der Adresse des Kunden. kann leer sein (null) · höchstens 255 Zeichen |
description
optional
|
string | Beschreibung der Arbeit oder der Beschwerde. kann leer sein (null) · höchstens 20000 Zeichen |
work_performed
optional
|
string | Was erledigt wurde. kann leer sein (null) · höchstens 20000 Zeichen |
customer_notes
optional
|
string | Hinweise für den Kunden; stehen auf dem Arbeitsauftrag. kann leer sein (null) · höchstens 20000 Zeichen |
scheduled_at
optional
|
string (date-time) | Wann die Arbeit geplant ist (UTC). Bei der Eingabe: ISO 8601 mit Zeitzone. kann leer sein (null) |
assigned_user_id
optional
|
string | Der Techniker oder Benutzer, der die Arbeit erledigt, oder null. kann leer sein (null) |
appointment_id
optional
|
string (uuid) | Der Termin, zu dem der Arbeitsauftrag gehört, oder null. kann leer sein (null) |
quote_id
optional
|
string (uuid) | Das Angebot, aus dem der Arbeitsauftrag stammt, oder null. kann leer sein (null) |
language
optional
|
string | Sprache des Arbeitsauftrags: nl, en, de oder fr. einer von: nl, en, de, fr |
items
optional
|
array | Die Positionen (höchstens 200). Bei der Eingabe: eine Liste mit pro Position name (erforderlich) und optional type, description, sku, quantity, unit, unit_price, discount_percentage, discount_amount, tax_rate, is_taxable und minutes; die Liste ersetzt alle Positionen. |
checklist
optional
|
array | Die Checkliste (höchstens 200 Punkte). Bei der Eingabe: pro Punkt label (erforderlich) und optional checked, required und note; die Liste ersetzt die ganze Checkliste. |
Beispielanfrage
curl -X POST "https://app.klantly.com/api/v1/work-orders" \
-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": "Onderhoud cv-ketel",
"status": "planned",
"scheduled_at": "2026-10-01T08:00:00Z",
"items": [
{
"type": "labor",
"name": "Arbeid",
"quantity": 2,
"unit_price": "45.00"
}
]
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'work-orders', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'customer_id' => '9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70',
'title' => 'Onderhoud cv-ketel',
'status' => 'planned',
'scheduled_at' => '2026-10-01T08:00:00Z',
'items' => [
0 => [
'type' => 'labor',
'name' => 'Arbeid',
'quantity' => 2,
'unit_price' => '45.00',
],
],
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/work-orders', {
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": "Onderhoud cv-ketel",
"status": "planned",
"scheduled_at": "2026-10-01T08:00:00Z",
"items": [
{
"type": "labor",
"name": "Arbeid",
"quantity": 2,
"unit_price": "45.00"
}
]
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/work-orders",
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": "Onderhoud cv-ketel",
"status": "planned",
"scheduled_at": "2026-10-01T08:00:00Z",
"items": [
{
"type": "labor",
"name": "Arbeid",
"quantity": 2,
"unit_price": "45.00"
}
]
},
)
data = response.json()["data"]Antwort 201
{
"data": {
"object": "work_order",
"id": "9d3f8328-9ed0-4f5d-8cab-b0cedfe0f1a9",
"number": "WB-2026-00042",
"status": "planned",
"title": "Onderhoud cv-ketel",
"type": "onderhoud",
"location": null,
"description": null,
"work_performed": null,
"customer_notes": 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"
},
"deal_id": null,
"appointment_id": null,
"quote_id": null,
"invoice_id": null,
"assigned_user_id": "usr_0k3j9x21m4zq8p",
"language": "nl",
"currency": "EUR",
"subtotal": "90.00",
"tax_amount": "18.90",
"total": "108.90",
"scheduled_at": "2026-10-01T08:00:00Z",
"started_at": null,
"completed_at": null,
"sent_at": null,
"signature": {
"is_signed": false,
"signed_by_name": null,
"signed_at": null
},
"items": [
{
"object": "work_order_item",
"id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cb",
"type": "labor",
"name": "Arbeid",
"description": null,
"sku": null,
"quantity": "2.00",
"unit": "uur",
"unit_price": "45.00",
"discount_percentage": "0.00",
"discount_amount": "0.00",
"line_total": "90.00",
"is_taxable": true,
"tax_rate": "21.00",
"minutes": 120
}
],
"checklist": [
{
"object": "work_order_checklist_item",
"id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dc",
"label": "Druk gecontroleerd",
"checked": false,
"required": true,
"note": null
}
],
"photos": [],
"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.
Arbeitsauftrag bearbeiten
/api/v1/work-orders/{work_order}
Ändert nur die Felder, die Sie mitsenden. items und checklist ersetzen die Positionen und die Checkliste als Ganzes; die Positionen erhalten dabei neue IDs. Eine Position und die Summe dürfen 99.999.999,99 nicht übersteigen. Ein abgerechneter Arbeitsauftrag kann nicht mehr geändert werden.
- Scope
-
work_orders.write— Arbeitsaufträge erstellen und bearbeiten sowie den Status ändern (Abschließen kann eine Bewertungsanfrage senden) - Erforderliche Funktion
work_orders
Senden Sie das ETag in If-Match mit, dann überschreiben Sie nie versehentlich eine neuere Version.
Pfadparameter
| Name | Typ | Beschreibung |
|---|---|---|
work_order erforderlich |
string (uuid) | Die ID (UUID) des Arbeitsauftrags. |
Body (JSON)
| Feld | Typ | Beschreibung |
|---|---|---|
title
optional
|
string | Titel. kann leer sein (null) · höchstens 255 Zeichen |
type
optional
|
string | Art der Arbeit, zum Beispiel Installation, Reparatur oder Wartung. kann leer sein (null) · höchstens 64 Zeichen |
location
optional
|
string | Wo die Arbeit stattfindet, wenn nicht an der Adresse des Kunden. kann leer sein (null) · höchstens 255 Zeichen |
description
optional
|
string | Beschreibung der Arbeit oder der Beschwerde. kann leer sein (null) · höchstens 20000 Zeichen |
work_performed
optional
|
string | Was erledigt wurde. kann leer sein (null) · höchstens 20000 Zeichen |
customer_notes
optional
|
string | Hinweise für den Kunden; stehen auf dem Arbeitsauftrag. kann leer sein (null) · höchstens 20000 Zeichen |
scheduled_at
optional
|
string (date-time) | Wann die Arbeit geplant ist (UTC). Bei der Eingabe: ISO 8601 mit Zeitzone. kann leer sein (null) |
assigned_user_id
optional
|
string | Der Techniker oder Benutzer, der die Arbeit erledigt, oder null. kann leer sein (null) |
appointment_id
optional
|
string (uuid) | Der Termin, zu dem der Arbeitsauftrag gehört, oder null. kann leer sein (null) |
quote_id
optional
|
string (uuid) | Das Angebot, aus dem der Arbeitsauftrag stammt, oder null. kann leer sein (null) |
language
optional
|
string | Sprache des Arbeitsauftrags: nl, en, de oder fr. einer von: nl, en, de, fr |
items
optional
|
array | Die Positionen (höchstens 200). Bei der Eingabe: eine Liste mit pro Position name (erforderlich) und optional type, description, sku, quantity, unit, unit_price, discount_percentage, discount_amount, tax_rate, is_taxable und minutes; die Liste ersetzt alle Positionen. |
checklist
optional
|
array | Die Checkliste (höchstens 200 Punkte). Bei der Eingabe: pro Punkt label (erforderlich) und optional checked, required und note; die Liste ersetzt die ganze Checkliste. |
Beispielanfrage
curl -X PATCH "https://app.klantly.com/api/v1/work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"work_performed": "Filter vervangen en de ketel ontlucht."
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('PATCH', 'work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', [
'json' => [
'work_performed' => 'Filter vervangen en de ketel ontlucht.',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"work_performed": "Filter vervangen en de ketel ontlucht."
}),
});
const { data } = await response.json();import os
import requests
response = requests.patch(
"https://app.klantly.com/api/v1/work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
json={
"work_performed": "Filter vervangen en de ketel ontlucht."
},
)
data = response.json()["data"]Antwort 200
{
"data": {
"object": "work_order",
"id": "9d3f8328-9ed0-4f5d-8cab-b0cedfe0f1a9",
"number": "WB-2026-00042",
"status": "planned",
"title": "Onderhoud cv-ketel",
"type": "onderhoud",
"location": null,
"description": null,
"work_performed": null,
"customer_notes": 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"
},
"deal_id": null,
"appointment_id": null,
"quote_id": null,
"invoice_id": null,
"assigned_user_id": "usr_0k3j9x21m4zq8p",
"language": "nl",
"currency": "EUR",
"subtotal": "90.00",
"tax_amount": "18.90",
"total": "108.90",
"scheduled_at": "2026-10-01T08:00:00Z",
"started_at": null,
"completed_at": null,
"sent_at": null,
"signature": {
"is_signed": false,
"signed_by_name": null,
"signed_at": null
},
"items": [
{
"object": "work_order_item",
"id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cb",
"type": "labor",
"name": "Arbeid",
"description": null,
"sku": null,
"quantity": "2.00",
"unit": "uur",
"unit_price": "45.00",
"discount_percentage": "0.00",
"discount_amount": "0.00",
"line_total": "90.00",
"is_taxable": true,
"tax_rate": "21.00",
"minutes": 120
}
],
"checklist": [
{
"object": "work_order_checklist_item",
"id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dc",
"label": "Druk gecontroleerd",
"checked": false,
"required": true,
"note": null
}
],
"photos": [],
"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. -
409
invalid_state_transition— Diese Aktion ist im aktuellen Status nicht möglich.
Status eines Arbeitsauftrags ändern
/api/v1/work-orders/{work_order}/status
Setzt den Arbeitsauftrag auf Entwurf, geplant, in Arbeit, abgeschlossen oder storniert. Bei in Arbeit und abgeschlossen werden started_at und completed_at gesetzt. Wie in der App kann das Abschließen dem Kunden eine Bewertungsanfrage senden, wenn das Unternehmen das eingerichtet hat. Abgerechnet setzt nur Klantly selbst. Ist in den Einstellungen eine Unterschrift erforderlich, kann ein nicht unterschriebener Arbeitsauftrag nicht auf abgeschlossen gesetzt werden (invalid_state_transition); das gilt auch beim Anlegen.
- Scope
-
work_orders.write— Arbeitsaufträge erstellen und bearbeiten sowie den Status ändern (Abschließen kann eine Bewertungsanfrage senden) - Erforderliche Funktion
work_orders
Senden Sie einen Idempotency-Key mit, dann erzeugt ein erneuter Versuch nach einem Timeout keinen doppelten Datensatz.
Pfadparameter
| Name | Typ | Beschreibung |
|---|---|---|
work_order erforderlich |
string (uuid) | Die ID (UUID) des Arbeitsauftrags. |
Body (JSON)
| Feld | Typ | Beschreibung |
|---|---|---|
status
erforderlich
|
string | draft (Entwurf), planned (geplant), in_progress (in Arbeit), completed (abgeschlossen), invoiced (abgerechnet) oder cancelled (storniert). Beim Erstellen draft oder planned. einer von: draft, planned, in_progress, completed, cancelled |
Beispielanfrage
curl -X POST "https://app.klantly.com/api/v1/work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/status" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f" \
-d '{
"status": "completed"
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/status', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'status' => 'completed',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/status', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
body: JSON.stringify({
"status": "completed"
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/status",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
json={
"status": "completed"
},
)
data = response.json()["data"]Antwort 200
{
"data": {
"object": "work_order",
"id": "9d3f8328-9ed0-4f5d-8cab-b0cedfe0f1a9",
"number": "WB-2026-00042",
"status": "planned",
"title": "Onderhoud cv-ketel",
"type": "onderhoud",
"location": null,
"description": null,
"work_performed": null,
"customer_notes": 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"
},
"deal_id": null,
"appointment_id": null,
"quote_id": null,
"invoice_id": null,
"assigned_user_id": "usr_0k3j9x21m4zq8p",
"language": "nl",
"currency": "EUR",
"subtotal": "90.00",
"tax_amount": "18.90",
"total": "108.90",
"scheduled_at": "2026-10-01T08:00:00Z",
"started_at": null,
"completed_at": null,
"sent_at": null,
"signature": {
"is_signed": false,
"signed_by_name": null,
"signed_at": null
},
"items": [
{
"object": "work_order_item",
"id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cb",
"type": "labor",
"name": "Arbeid",
"description": null,
"sku": null,
"quantity": "2.00",
"unit": "uur",
"unit_price": "45.00",
"discount_percentage": "0.00",
"discount_amount": "0.00",
"line_total": "90.00",
"is_taxable": true,
"tax_rate": "21.00",
"minutes": 120
}
],
"checklist": [
{
"object": "work_order_checklist_item",
"id": "9d3f868b-c1a3-4c8a-9fde-e3f1a2b3c4dc",
"label": "Druk gecontroleerd",
"checked": false,
"required": true,
"note": null
}
],
"photos": [],
"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.
Arbeitsauftrag löschen
/api/v1/work-orders/{work_order}
Löscht den Arbeitsauftrag. Ein abgerechneter Arbeitsauftrag kann nicht gelöscht werden.
- Scope
-
work_orders.delete— Arbeitsaufträge löschen - Erforderliche Funktion
work_orders
Pfadparameter
| Name | Typ | Beschreibung |
|---|---|---|
work_order erforderlich |
string (uuid) | Die ID (UUID) des Arbeitsauftrags. |
Beispielanfrage
curl -X DELETE "https://app.klantly.com/api/v1/work-orders/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', 'work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/work-orders/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/work-orders/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. -
409
invalid_state_transition— Diese Aktion ist im aktuellen Status nicht möglich.
Das Objekt
Alle Felder sind immer vorhanden; ein Feld ohne Wert ist null.
| Feld | Typ | Beschreibung |
|---|---|---|
object |
string | Immer „work_order“. |
id |
string (uuid) | Eindeutige ID (UUID). |
number |
string | Nummer des Arbeitsauftrags, zum Beispiel WB-2026-00042; vergibt Klantly selbst. |
status |
string | draft (Entwurf), planned (geplant), in_progress (in Arbeit), completed (abgeschlossen), invoiced (abgerechnet) oder cancelled (storniert). Beim Erstellen draft oder planned. einer von: draft, planned, in_progress, completed, invoiced, cancelled |
title |
string | Titel. kann leer sein (null) |
type |
string | Art der Arbeit, zum Beispiel Installation, Reparatur oder Wartung. kann leer sein (null) |
location |
string | Wo die Arbeit stattfindet, wenn nicht an der Adresse des Kunden. kann leer sein (null) |
description |
string | Beschreibung der Arbeit oder der Beschwerde. kann leer sein (null) |
work_performed |
string | Was erledigt wurde. kann leer sein (null) |
customer_notes |
string | Hinweise für den Kunden; stehen auf dem Arbeitsauftrag. 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 Arbeitsauftrag, 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) |
deal_id |
string (uuid) | Der Deal auf dem Pipeline-Board, oder null. kann leer sein (null) |
appointment_id |
string (uuid) | Der Termin, zu dem der Arbeitsauftrag gehört, oder null. kann leer sein (null) |
quote_id |
string (uuid) | Das Angebot, aus dem der Arbeitsauftrag stammt, oder null. kann leer sein (null) |
invoice_id |
string (uuid) | Die Rechnung des Arbeitsauftrags, oder null. kann leer sein (null) |
assigned_user_id |
string | Der Techniker oder Benutzer, der die Arbeit erledigt, oder null. kann leer sein (null) |
language |
string | Sprache des Arbeitsauftrags: nl, en, de oder fr. einer von: nl, en, de, fr |
currency |
string | Immer „EUR“. |
subtotal |
string | Summe ohne Umsatzsteuer, als Text mit zwei Dezimalstellen. |
tax_amount |
string | Umsatzsteuerbetrag. |
total |
string | Summe inklusive Umsatzsteuer. |
scheduled_at |
string (date-time) | Wann die Arbeit geplant ist (UTC). Bei der Eingabe: ISO 8601 mit Zeitzone. kann leer sein (null) |
started_at |
string (date-time) | Wann die Arbeit begann. kann leer sein (null) |
completed_at |
string (date-time) | Wann die Arbeit fertig war. kann leer sein (null) |
sent_at |
string (date-time) | Wann der Arbeitsauftrag an den Kunden gesendet wurde. kann leer sein (null) |
signature |
object | Die Unterschrift des Kunden (nur die Daten, kein Bild). |
signature.is_signed |
boolean | Ob der Arbeitsauftrag unterschrieben ist. |
signature.signed_by_name |
string | Name der Person, die unterschrieben hat. kann leer sein (null) |
signature.signed_at |
string (date-time) | Wann unterschrieben wurde. kann leer sein (null) |
items |
array<object> | Die Positionen (höchstens 200). Bei der Eingabe: eine Liste mit pro Position name (erforderlich) und optional type, description, sku, quantity, unit, unit_price, discount_percentage, discount_amount, tax_rate, is_taxable und minutes; die Liste ersetzt alle Positionen. |
items.object |
string | Immer „work_order_item“. |
items.id |
string (uuid) | ID der Position. Ändert sich jedes Mal, wenn Sie items senden (die Positionen werden ersetzt). |
items.type |
string | labor (Arbeit), material (Material) oder other. einer von: labor, material, other |
items.name |
string | Bezeichnung auf dem Arbeitsauftrag. |
items.description |
string | Erläuterung. kann leer sein (null) |
items.sku |
string | Artikelnummer. kann leer sein (null) |
items.quantity |
string | Menge. |
items.unit |
string | Einheit, zum Beispiel Stunde oder Stück. kann leer sein (null) |
items.unit_price |
string | Preis pro Einheit ohne Umsatzsteuer. |
items.discount_percentage |
string | Rabatt in Prozent. |
items.discount_amount |
string | Fester Rabatt auf die Position ohne Umsatzsteuer; zählt nur, wenn discount_percentage 0 ist. |
items.line_total |
string | Positionssumme ohne Umsatzsteuer; berechnet Klantly. |
items.is_taxable |
boolean | Ob die Position der Umsatzsteuer unterliegt. |
items.tax_rate |
string | Steuersatz in Prozent (Standard 21, auch wenn das Unternehmen einen anderen Satz verwendet: senden Sie ihn dann mit). |
items.minutes |
integer | Gearbeitete Minuten, bei Arbeit. kann leer sein (null) |
checklist |
array<object> | Die Checkliste (höchstens 200 Punkte). Bei der Eingabe: pro Punkt label (erforderlich) und optional checked, required und note; die Liste ersetzt die ganze Checkliste. |
checklist.object |
string | Immer „work_order_checklist_item“. |
checklist.id |
string (uuid) | ID des Punkts. |
checklist.label |
string | Der Punkt, zum Beispiel „Druck geprüft“. |
checklist.checked |
boolean | Abgehakt. |
checklist.required |
boolean | Muss abgehakt werden. |
checklist.note |
string | Anmerkung zum Punkt. kann leer sein (null) |
photos |
array<object> | Die Fotos zum Arbeitsauftrag, jeweils mit einem signierten Download-Link. Hinzufügen und Löschen läuft über Foto zum Arbeitsauftrag hochladen und Foto des Arbeitsauftrags löschen. |
photos.object |
string | Immer „work_order_photo“. |
photos.id |
string (uuid) | ID des Fotos. |
photos.kind |
string | before (vorher), after (nachher) oder other. kann leer sein (null) · einer von: before, after, other |
photos.caption |
string | Bildunterschrift. kann leer sein (null) |
photos.download_url |
string (uri) | Ein signierter Link zum Foto, fünfzehn Minuten gültig und ohne API-Schlüssel zu öffnen. |
photos.created_at |
string (date-time) | Hochgeladen am (UTC). |
created_at |
string (date-time) | Erstellt am (UTC). |
updated_at |
string (date-time) | Zuletzt geändert am (UTC). |