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, of zoek met q op nummer, klantnaam en titel. 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. |
q
|
string | Zoek in het werkbonnummer, de klantnaam en de titel. minimaal 2 tekens · maximaal 100 tekens |
sort
|
string | Het veld waarop je sorteert; een min-teken ervoor is aflopend. Welke velden je kunt kiezen en wat de standaard is, staat bij deze parameter. 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. Neem een overlap van een paar minuten op je vorige moment, omdat de klok van jouw systeem en die van Klantly niet exact gelijk lopen, en omdat een wijziging vlak na je vorige moment anders net buiten de boot valt. De grens is inclusief: iets dat exact op dit tijdstip is gewijzigd, doet mee. |
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 (met downloadlink) 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. Staat in de werkbon-instellingen "handtekening verplicht" aan, dan kan een niet-ondertekende werkbon niet op afgerond (invalid_state_transition); dat geldt ook bij aanmaken.
- 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, elk met een ondertekende downloadlink. Toevoegen en verwijderen gaat met Werkbonfoto uploaden en Werkbonfoto verwijderen. |
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.download_url |
string (uri) | Een ondertekende link naar de foto, een kwartier geldig en zonder API-sleutel te openen. |
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). |