API-referentie
Werkbonnen
Werkbonnen met regels en checklist: aanmaken vanuit bijvoorbeeld een ERP, bijwerken, de status wijzigen en afgeronde werkbonnen ophalen.
Endpoints
-
GET
/work-ordersWerkbonnen opvragen -
GET
/work-orders/{work_order}Werkbon ophalen -
POST
/work-ordersWerkbon aanmaken -
PATCH
/work-orders/{work_order}Werkbon bijwerken -
POST
/work-orders/{work_order}/statusStatus van een werkbon wijzigen -
DELETE
/work-orders/{work_order}Werkbon verwijderen
Werkbonnen opvragen
/api/v1/work-orders
Een lijst van werkbonnen, nieuwste eerst, met regels, checklist en foto’s. Filter op status, klant, gebruiker, afspraak of wijzigingsdatum. Met filter[status]=completed en filter[updated_since] haal je afgerond werk op.
- Scope
-
work_orders.read— Werkbonnen lezen, met naam, adres en contactgegevens van de klant - Vereiste functie
work_orders
Queryparameters
| Naam | Type | Omschrijving |
|---|---|---|
limit
|
integer | Aantal resultaten per pagina. van 1 tot 100 · standaard: 50 |
cursor
|
string | De next_cursor of prev_cursor uit meta van het vorige antwoord. |
sort
|
string | Sortering op created_at of updated_at; een min-teken ervoor is aflopend. een van: -created_at, created_at, -updated_at, updated_at · standaard: -created_at |
filter[status]
|
string | Alleen werkbonnen met deze status. een van: draft, planned, in_progress, completed, invoiced, cancelled |
filter[customer_id]
|
string (uuid) | Alleen wat bij deze klant hoort. |
filter[assigned_user_id]
|
string | Alleen wat aan deze gebruiker is toegewezen (de id uit Gebruikers opvragen). |
filter[appointment_id]
|
string (uuid) | Alleen werkbonnen bij deze afspraak. |
filter[updated_since]
|
string (date-time) | Alleen wat sinds dit tijdstip is gewijzigd: ISO 8601 mét tijdzone, bijvoorbeeld 2026-09-14T10:15:00Z. Handig om te synchroniseren. |
Voorbeeldverzoek
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"]Antwoord 200
Het antwoord is een lijst met cursorpaginering: data bevat de objecten, meta de paginering.
{
"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
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie. -
422
validation_failed— De invoer is ongeldig.
Werkbon ophalen
/api/v1/work-orders/{work_order}
Eén werkbon op id, met regels, checklist, foto’s (alleen de gegevens) en de stand van de handtekening.
- Scope
-
work_orders.read— Werkbonnen lezen, met naam, adres en contactgegevens van de klant - Vereiste functie
work_orders
Padparameters
| Naam | Type | Omschrijving |
|---|---|---|
work_order verplicht |
string (uuid) | De id (UUID) van de werkbon. |
Voorbeeldverzoek
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"]Antwoord 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"
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie. -
404
not_found— Niet gevonden.
Werkbon aanmaken
/api/v1/work-orders
Maakt een werkbon aan bij een klant, als concept of ingepland. Klantly geeft hem een nummer en rekent de totalen uit; naam, adres en contact komen van de klant.
- Scope
-
work_orders.write— Werkbonnen aanmaken en wijzigen, en de status aanpassen (afronden kan een reviewverzoek sturen) - Vereiste functie
work_orders
Stuur een Idempotency-Key mee, dan maakt een nieuwe poging na een time-out geen dubbel record.
Body (JSON)
| Veld | Type | Omschrijving |
|---|---|---|
customer_id
verplicht
|
string (uuid) | De klant. Verplicht bij aanmaken; naam, adres en contact komen van de klant. |
status
optioneel
|
string | draft (concept), planned (ingepland), in_progress (bezig), completed (afgerond), invoiced (gefactureerd) of cancelled (geannuleerd). Bij aanmaken draft of planned. een van: draft, planned |
title
optioneel
|
string | Titel. kan leeg zijn (null) · maximaal 255 tekens |
type
optioneel
|
string | Soort werk, bijvoorbeeld installatie, reparatie of onderhoud. kan leeg zijn (null) · maximaal 64 tekens |
location
optioneel
|
string | Waar het werk plaatsvindt, als dat niet het adres van de klant is. kan leeg zijn (null) · maximaal 255 tekens |
description
optioneel
|
string | Omschrijving van het werk of de klacht. kan leeg zijn (null) · maximaal 20000 tekens |
work_performed
optioneel
|
string | Wat er is gedaan. kan leeg zijn (null) · maximaal 20000 tekens |
customer_notes
optioneel
|
string | Opmerkingen voor de klant; staan op de werkbon. kan leeg zijn (null) · maximaal 20000 tekens |
scheduled_at
optioneel
|
string (date-time) | Wanneer het werk gepland staat (UTC). Bij invoer: ISO 8601 mét tijdzone. kan leeg zijn (null) |
assigned_user_id
optioneel
|
string | De monteur of gebruiker die het werk doet, of null. kan leeg zijn (null) |
appointment_id
optioneel
|
string (uuid) | De afspraak waar de werkbon bij hoort, of null. kan leeg zijn (null) |
quote_id
optioneel
|
string (uuid) | De offerte waar de werkbon uit komt, of null. kan leeg zijn (null) |
language
optioneel
|
string | Taal van de werkbon: nl, en, de of fr. een van: nl, en, de, fr |
items
optioneel
|
array | De regels (maximaal 200). Bij invoer: een lijst met per regel name (verplicht) en optioneel type, description, sku, quantity, unit, unit_price, discount_percentage, discount_amount, tax_rate, is_taxable en minutes; de lijst vervangt alle regels. |
checklist
optioneel
|
array | De checklist (maximaal 200 punten). Bij invoer: per punt label (verplicht) en optioneel checked, required en note; de lijst vervangt de hele checklist. |
Voorbeeldverzoek
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"]Antwoord 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"
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie. -
422
validation_failed— De invoer is ongeldig. -
422
unknown_field— De invoer bevat een onbekend veld. -
415
unsupported_media_type— Dit formaat wordt niet ondersteund. -
413
payload_too_large— De body van het verzoek is te groot. -
422
idempotency_key_reused— Deze Idempotency-Key is al gebruikt voor een ander verzoek. -
409
idempotency_in_progress— Een verzoek met deze Idempotency-Key is nog bezig.
Werkbon bijwerken
/api/v1/work-orders/{work_order}
Wijzigt alleen de velden die je meestuurt. items en checklist vervangen de regels en de checklist als geheel; de regels krijgen daarbij nieuwe id's. Een regel en het totaal mogen niet boven 99.999.999,99 uitkomen. Een gefactureerde werkbon kan niet meer veranderen.
- Scope
-
work_orders.write— Werkbonnen aanmaken en wijzigen, en de status aanpassen (afronden kan een reviewverzoek sturen) - Vereiste functie
work_orders
Stuur de ETag mee in If-Match, dan overschrijf je nooit per ongeluk een nieuwere versie.
Padparameters
| Naam | Type | Omschrijving |
|---|---|---|
work_order verplicht |
string (uuid) | De id (UUID) van de werkbon. |
Body (JSON)
| Veld | Type | Omschrijving |
|---|---|---|
title
optioneel
|
string | Titel. kan leeg zijn (null) · maximaal 255 tekens |
type
optioneel
|
string | Soort werk, bijvoorbeeld installatie, reparatie of onderhoud. kan leeg zijn (null) · maximaal 64 tekens |
location
optioneel
|
string | Waar het werk plaatsvindt, als dat niet het adres van de klant is. kan leeg zijn (null) · maximaal 255 tekens |
description
optioneel
|
string | Omschrijving van het werk of de klacht. kan leeg zijn (null) · maximaal 20000 tekens |
work_performed
optioneel
|
string | Wat er is gedaan. kan leeg zijn (null) · maximaal 20000 tekens |
customer_notes
optioneel
|
string | Opmerkingen voor de klant; staan op de werkbon. kan leeg zijn (null) · maximaal 20000 tekens |
scheduled_at
optioneel
|
string (date-time) | Wanneer het werk gepland staat (UTC). Bij invoer: ISO 8601 mét tijdzone. kan leeg zijn (null) |
assigned_user_id
optioneel
|
string | De monteur of gebruiker die het werk doet, of null. kan leeg zijn (null) |
appointment_id
optioneel
|
string (uuid) | De afspraak waar de werkbon bij hoort, of null. kan leeg zijn (null) |
quote_id
optioneel
|
string (uuid) | De offerte waar de werkbon uit komt, of null. kan leeg zijn (null) |
language
optioneel
|
string | Taal van de werkbon: nl, en, de of fr. een van: nl, en, de, fr |
items
optioneel
|
array | De regels (maximaal 200). Bij invoer: een lijst met per regel name (verplicht) en optioneel type, description, sku, quantity, unit, unit_price, discount_percentage, discount_amount, tax_rate, is_taxable en minutes; de lijst vervangt alle regels. |
checklist
optioneel
|
array | De checklist (maximaal 200 punten). Bij invoer: per punt label (verplicht) en optioneel checked, required en note; de lijst vervangt de hele checklist. |
Voorbeeldverzoek
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"]Antwoord 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"
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie. -
422
validation_failed— De invoer is ongeldig. -
422
unknown_field— De invoer bevat een onbekend veld. -
415
unsupported_media_type— Dit formaat wordt niet ondersteund. -
413
payload_too_large— De body van het verzoek is te groot. -
404
not_found— Niet gevonden. -
412
precondition_failed— Het record is intussen gewijzigd. -
409
invalid_state_transition— Deze actie kan niet in de huidige status.
Status van een werkbon wijzigen
/api/v1/work-orders/{work_order}/status
Zet de werkbon op concept, ingepland, bezig, afgerond of geannuleerd. Bij bezig en afgerond worden started_at en completed_at gezet. Afronden kan, net als in de app, een reviewverzoek naar de klant sturen als het bedrijf dat heeft ingesteld. Gefactureerd zet alleen Klantly zelf.
- Scope
-
work_orders.write— Werkbonnen aanmaken en wijzigen, en de status aanpassen (afronden kan een reviewverzoek sturen) - Vereiste functie
work_orders
Stuur een Idempotency-Key mee, dan maakt een nieuwe poging na een time-out geen dubbel record.
Padparameters
| Naam | Type | Omschrijving |
|---|---|---|
work_order verplicht |
string (uuid) | De id (UUID) van de werkbon. |
Body (JSON)
| Veld | Type | Omschrijving |
|---|---|---|
status
verplicht
|
string | draft (concept), planned (ingepland), in_progress (bezig), completed (afgerond), invoiced (gefactureerd) of cancelled (geannuleerd). Bij aanmaken draft of planned. een van: draft, planned, in_progress, completed, cancelled |
Voorbeeldverzoek
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"]Antwoord 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"
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie. -
422
validation_failed— De invoer is ongeldig. -
422
unknown_field— De invoer bevat een onbekend veld. -
415
unsupported_media_type— Dit formaat wordt niet ondersteund. -
413
payload_too_large— De body van het verzoek is te groot. -
404
not_found— Niet gevonden. -
409
invalid_state_transition— Deze actie kan niet in de huidige status. -
422
idempotency_key_reused— Deze Idempotency-Key is al gebruikt voor een ander verzoek. -
409
idempotency_in_progress— Een verzoek met deze Idempotency-Key is nog bezig.
Werkbon verwijderen
/api/v1/work-orders/{work_order}
Verwijdert de werkbon. Een gefactureerde werkbon kan niet worden verwijderd.
- Scope
-
work_orders.delete— Werkbonnen verwijderen - Vereiste functie
work_orders
Padparameters
| Naam | Type | Omschrijving |
|---|---|---|
work_order verplicht |
string (uuid) | De id (UUID) van de werkbon. |
Voorbeeldverzoek
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"]Antwoord 200
{
"data": {
"object": "note",
"id": "9d3f7b41-2d6f-4e8c-9b3a-4f5d6e7a8b92",
"deleted": true
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie. -
404
not_found— Niet gevonden. -
409
invalid_state_transition— Deze actie kan niet in de huidige status.
Het object
Alle velden zijn altijd aanwezig; een veld zonder waarde is null.
| Veld | Type | Omschrijving |
|---|---|---|
object |
string | Altijd "work_order". |
id |
string (uuid) | Unieke id (UUID). |
number |
string | Werkbonnummer, bijvoorbeeld WB-2026-00042; geeft Klantly zelf. |
status |
string | draft (concept), planned (ingepland), in_progress (bezig), completed (afgerond), invoiced (gefactureerd) of cancelled (geannuleerd). Bij aanmaken draft of planned. een van: draft, planned, in_progress, completed, invoiced, cancelled |
title |
string | Titel. kan leeg zijn (null) |
type |
string | Soort werk, bijvoorbeeld installatie, reparatie of onderhoud. kan leeg zijn (null) |
location |
string | Waar het werk plaatsvindt, als dat niet het adres van de klant is. kan leeg zijn (null) |
description |
string | Omschrijving van het werk of de klacht. kan leeg zijn (null) |
work_performed |
string | Wat er is gedaan. kan leeg zijn (null) |
customer_notes |
string | Opmerkingen voor de klant; staan op de werkbon. kan leeg zijn (null) |
customer_id |
string (uuid) | De klant. Verplicht bij aanmaken; naam, adres en contact komen van de klant. kan leeg zijn (null) |
customer |
object | De klantgegevens op de werkbon, zoals ze bij het aanmaken waren. |
customer.type |
string | individual (particulier) of business (bedrijf). kan leeg zijn (null) |
customer.name |
string | Naam; bij een bedrijf de bedrijfsnaam. kan leeg zijn (null) |
customer.email |
string | E-mailadres. kan leeg zijn (null) |
customer.phone |
string | Telefoonnummer. kan leeg zijn (null) |
customer.address |
string | Straat en huisnummer. kan leeg zijn (null) |
customer.postal_code |
string | Postcode. kan leeg zijn (null) |
customer.city |
string | Plaats. kan leeg zijn (null) |
customer.country |
string | Land. kan leeg zijn (null) |
deal_id |
string (uuid) | De deal op het pipelinebord, of null. kan leeg zijn (null) |
appointment_id |
string (uuid) | De afspraak waar de werkbon bij hoort, of null. kan leeg zijn (null) |
quote_id |
string (uuid) | De offerte waar de werkbon uit komt, of null. kan leeg zijn (null) |
invoice_id |
string (uuid) | De factuur van de werkbon, of null. kan leeg zijn (null) |
assigned_user_id |
string | De monteur of gebruiker die het werk doet, of null. kan leeg zijn (null) |
language |
string | Taal van de werkbon: nl, en, de of fr. een van: nl, en, de, fr |
currency |
string | Altijd "EUR". |
subtotal |
string | Totaal zonder btw, als tekst met twee decimalen. |
tax_amount |
string | Btw-bedrag. |
total |
string | Totaal inclusief btw. |
scheduled_at |
string (date-time) | Wanneer het werk gepland staat (UTC). Bij invoer: ISO 8601 mét tijdzone. kan leeg zijn (null) |
started_at |
string (date-time) | Wanneer het werk begon. kan leeg zijn (null) |
completed_at |
string (date-time) | Wanneer het werk klaar was. kan leeg zijn (null) |
sent_at |
string (date-time) | Wanneer de werkbon naar de klant is gestuurd. kan leeg zijn (null) |
signature |
object | De handtekening van de klant (alleen de gegevens, geen afbeelding). |
signature.is_signed |
boolean | Is de werkbon ondertekend. |
signature.signed_by_name |
string | Naam van wie ondertekende. kan leeg zijn (null) |
signature.signed_at |
string (date-time) | Wanneer er werd ondertekend. kan leeg zijn (null) |
items |
array<object> | De regels (maximaal 200). Bij invoer: een lijst met per regel name (verplicht) en optioneel type, description, sku, quantity, unit, unit_price, discount_percentage, discount_amount, tax_rate, is_taxable en minutes; de lijst vervangt alle regels. |
items.object |
string | Altijd "work_order_item". |
items.id |
string (uuid) | Id van de regel. Verandert telkens als je items meestuurt (de regels worden vervangen). |
items.type |
string | labor (arbeid), material (materiaal) of other. een van: labor, material, other |
items.name |
string | Omschrijving op de werkbon. |
items.description |
string | Toelichting. kan leeg zijn (null) |
items.sku |
string | Artikelnummer. kan leeg zijn (null) |
items.quantity |
string | Aantal. |
items.unit |
string | Eenheid, bijvoorbeeld uur of stuk. kan leeg zijn (null) |
items.unit_price |
string | Prijs per eenheid zonder btw. |
items.discount_percentage |
string | Korting in procenten. |
items.discount_amount |
string | Vaste korting op de regel zonder btw; telt alleen als discount_percentage 0 is. |
items.line_total |
string | Regeltotaal zonder btw; rekent Klantly uit. |
items.is_taxable |
boolean | Valt de regel onder de btw. |
items.tax_rate |
string | Btw-percentage (standaard 21, ook als het bedrijf een ander tarief gebruikt: stuur het dan mee). |
items.minutes |
integer | Gewerkte minuten, bij arbeid. kan leeg zijn (null) |
checklist |
array<object> | De checklist (maximaal 200 punten). Bij invoer: per punt label (verplicht) en optioneel checked, required en note; de lijst vervangt de hele checklist. |
checklist.object |
string | Altijd "work_order_checklist_item". |
checklist.id |
string (uuid) | Id van het punt. |
checklist.label |
string | Het punt, bijvoorbeeld "Druk gecontroleerd". |
checklist.checked |
boolean | Afgevinkt. |
checklist.required |
boolean | Verplicht om af te vinken. |
checklist.note |
string | Opmerking bij het punt. kan leeg zijn (null) |
photos |
array<object> | De foto’s bij de werkbon (alleen de gegevens; de bestanden zitten nog niet in de API). |
photos.object |
string | Altijd "work_order_photo". |
photos.id |
string (uuid) | Id van de foto. |
photos.kind |
string | before (voor), after (na) of other. kan leeg zijn (null) · een van: before, after, other |
photos.caption |
string | Bijschrift. kan leeg zijn (null) |
photos.created_at |
string (date-time) | Geüpload op (UTC). |
created_at |
string (date-time) | Aangemaakt op (UTC). |
updated_at |
string (date-time) | Laatst gewijzigd op (UTC). |