Référence de l'API
Bons d’intervention
Des bons d’intervention avec lignes et check-list : les créer depuis un ERP par exemple, les modifier, changer leur statut et récupérer ceux qui sont terminés.
Endpoints
-
GET
/work-ordersLister les bons d’intervention -
GET
/work-orders/{work_order}Récupérer un bon d’intervention -
POST
/work-ordersCréer un bon d’intervention -
PATCH
/work-orders/{work_order}Modifier un bon d’intervention -
POST
/work-orders/{work_order}/statusChanger le statut d’un bon d’intervention -
DELETE
/work-orders/{work_order}Supprimer un bon d’intervention
Lister les bons d’intervention
/api/v1/work-orders
Une liste de bons d’intervention, du plus récent au plus ancien, avec lignes, check-list et photos. Filtrez par statut, client, utilisateur, rendez-vous ou date de modification. Avec filter[status]=completed et filter[updated_since], vous récupérez le travail terminé.
- Scope
-
work_orders.read— Lire les bons d'intervention, avec le nom, l'adresse et les coordonnées du client - Fonctionnalité requise
work_orders
Paramètres de requête
| Nom | Type | Description |
|---|---|---|
limit
|
integer | Nombre de résultats par page. de 1 à 100 · par défaut : 50 |
cursor
|
string | Le next_cursor ou prev_cursor de meta dans la réponse précédente. |
sort
|
string | Tri par created_at ou updated_at ; un signe moins devant trie par ordre décroissant. l'une des valeurs : -created_at, created_at, -updated_at, updated_at · par défaut : -created_at |
filter[status]
|
string | Uniquement les bons d’intervention de ce statut. l'une des valeurs : draft, planned, in_progress, completed, invoiced, cancelled |
filter[customer_id]
|
string (uuid) | Uniquement ce qui appartient à ce client. |
filter[assigned_user_id]
|
string | Uniquement ce qui est assigné à cet utilisateur (l’id de Lister les utilisateurs). |
filter[appointment_id]
|
string (uuid) | Uniquement les bons d’intervention de ce rendez-vous. |
filter[updated_since]
|
string (date-time) | Uniquement ce qui a changé depuis ce moment : ISO 8601 avec fuseau horaire, par exemple 2026-09-14T10:15:00Z. Pratique pour synchroniser. |
Exemple de requête
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"]Réponse 200
La réponse est une liste avec pagination par curseur : data contient les objets, meta la pagination.
{
"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
}
}Erreurs possibles
-
403
insufficient_scope— Cette clé API n'a pas accès à cette action. -
422
validation_failed— Les données saisies ne sont pas valides.
Récupérer un bon d’intervention
/api/v1/work-orders/{work_order}
Un bon d’intervention par id, avec lignes, check-list, photos (données uniquement) et l’état de la signature.
- Scope
-
work_orders.read— Lire les bons d'intervention, avec le nom, l'adresse et les coordonnées du client - Fonctionnalité requise
work_orders
Paramètres de chemin
| Nom | Type | Description |
|---|---|---|
work_order obligatoire |
string (uuid) | L’id (UUID) du bon d’intervention. |
Exemple de requête
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"]Réponse 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"
}
}Erreurs possibles
-
403
insufficient_scope— Cette clé API n'a pas accès à cette action. -
404
not_found— Introuvable.
Créer un bon d’intervention
/api/v1/work-orders
Crée un bon d’intervention pour un client, en brouillon ou planifié. Klantly lui attribue un numéro et calcule les totaux ; le nom, l’adresse et les coordonnées viennent du client.
- Scope
-
work_orders.write— Créer et modifier des bons d'intervention, et changer leur statut (terminer peut envoyer une demande d'avis) - Fonctionnalité requise
work_orders
Envoyez une Idempotency-Key : une nouvelle tentative après un délai d'attente ne crée alors jamais de doublon.
Corps (JSON)
| Champ | Type | Description |
|---|---|---|
customer_id
obligatoire
|
string (uuid) | Le client. Obligatoire à la création ; le nom, l’adresse et les coordonnées viennent du client. |
status
facultatif
|
string | draft (brouillon), planned (planifié), in_progress (en cours), completed (terminé), invoiced (facturé) ou cancelled (annulé). À la création : draft ou planned. l'une des valeurs : draft, planned |
title
facultatif
|
string | Titre. peut être vide (null) · au maximum 255 caractères |
type
facultatif
|
string | Type de travail, par exemple installation, réparation ou entretien. peut être vide (null) · au maximum 64 caractères |
location
facultatif
|
string | Lieu de l’intervention, s’il ne s’agit pas de l’adresse du client. peut être vide (null) · au maximum 255 caractères |
description
facultatif
|
string | Description du travail ou de la réclamation. peut être vide (null) · au maximum 20000 caractères |
work_performed
facultatif
|
string | Ce qui a été fait. peut être vide (null) · au maximum 20000 caractères |
customer_notes
facultatif
|
string | Remarques pour le client ; figurent sur le bon. peut être vide (null) · au maximum 20000 caractères |
scheduled_at
facultatif
|
string (date-time) | Date prévue de l’intervention (UTC). En entrée : ISO 8601 avec fuseau horaire. peut être vide (null) |
assigned_user_id
facultatif
|
string | Le technicien ou l’utilisateur qui réalise le travail, ou null. peut être vide (null) |
appointment_id
facultatif
|
string (uuid) | Le rendez-vous auquel le bon appartient, ou null. peut être vide (null) |
quote_id
facultatif
|
string (uuid) | Le devis dont provient le bon, ou null. peut être vide (null) |
language
facultatif
|
string | Langue du bon : nl, en, de ou fr. l'une des valeurs : nl, en, de, fr |
items
facultatif
|
array | Les lignes (200 au maximum). En entrée : une liste avec par ligne name (obligatoire) et en option type, description, sku, quantity, unit, unit_price, discount_percentage, discount_amount, tax_rate, is_taxable et minutes ; la liste remplace toutes les lignes. |
checklist
facultatif
|
array | La check-list (200 points au maximum). En entrée : par point label (obligatoire) et en option checked, required et note ; la liste remplace toute la check-list. |
Exemple de requête
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"]Réponse 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"
}
}Erreurs possibles
-
403
insufficient_scope— Cette clé API n'a pas accès à cette action. -
422
validation_failed— Les données saisies ne sont pas valides. -
422
unknown_field— Les données contiennent un champ inconnu. -
415
unsupported_media_type— Ce format n'est pas pris en charge. -
413
payload_too_large— Le corps de la requête est trop volumineux. -
422
idempotency_key_reused— Cette Idempotency-Key a déjà été utilisée pour une autre requête. -
409
idempotency_in_progress— Une requête avec cette Idempotency-Key est encore en cours.
Modifier un bon d’intervention
/api/v1/work-orders/{work_order}
Ne modifie que les champs envoyés. items et checklist remplacent l’ensemble des lignes et de la check-list ; les lignes reçoivent de nouveaux id. Une ligne et le total ne peuvent pas dépasser 99 999 999,99. Un bon d’intervention facturé ne peut plus être modifié.
- Scope
-
work_orders.write— Créer et modifier des bons d'intervention, et changer leur statut (terminer peut envoyer une demande d'avis) - Fonctionnalité requise
work_orders
Envoyez l'ETag dans If-Match : vous n'écraserez jamais par erreur une version plus récente.
Paramètres de chemin
| Nom | Type | Description |
|---|---|---|
work_order obligatoire |
string (uuid) | L’id (UUID) du bon d’intervention. |
Corps (JSON)
| Champ | Type | Description |
|---|---|---|
title
facultatif
|
string | Titre. peut être vide (null) · au maximum 255 caractères |
type
facultatif
|
string | Type de travail, par exemple installation, réparation ou entretien. peut être vide (null) · au maximum 64 caractères |
location
facultatif
|
string | Lieu de l’intervention, s’il ne s’agit pas de l’adresse du client. peut être vide (null) · au maximum 255 caractères |
description
facultatif
|
string | Description du travail ou de la réclamation. peut être vide (null) · au maximum 20000 caractères |
work_performed
facultatif
|
string | Ce qui a été fait. peut être vide (null) · au maximum 20000 caractères |
customer_notes
facultatif
|
string | Remarques pour le client ; figurent sur le bon. peut être vide (null) · au maximum 20000 caractères |
scheduled_at
facultatif
|
string (date-time) | Date prévue de l’intervention (UTC). En entrée : ISO 8601 avec fuseau horaire. peut être vide (null) |
assigned_user_id
facultatif
|
string | Le technicien ou l’utilisateur qui réalise le travail, ou null. peut être vide (null) |
appointment_id
facultatif
|
string (uuid) | Le rendez-vous auquel le bon appartient, ou null. peut être vide (null) |
quote_id
facultatif
|
string (uuid) | Le devis dont provient le bon, ou null. peut être vide (null) |
language
facultatif
|
string | Langue du bon : nl, en, de ou fr. l'une des valeurs : nl, en, de, fr |
items
facultatif
|
array | Les lignes (200 au maximum). En entrée : une liste avec par ligne name (obligatoire) et en option type, description, sku, quantity, unit, unit_price, discount_percentage, discount_amount, tax_rate, is_taxable et minutes ; la liste remplace toutes les lignes. |
checklist
facultatif
|
array | La check-list (200 points au maximum). En entrée : par point label (obligatoire) et en option checked, required et note ; la liste remplace toute la check-list. |
Exemple de requête
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"]Réponse 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"
}
}Erreurs possibles
-
403
insufficient_scope— Cette clé API n'a pas accès à cette action. -
422
validation_failed— Les données saisies ne sont pas valides. -
422
unknown_field— Les données contiennent un champ inconnu. -
415
unsupported_media_type— Ce format n'est pas pris en charge. -
413
payload_too_large— Le corps de la requête est trop volumineux. -
404
not_found— Introuvable. -
412
precondition_failed— L'enregistrement a été modifié entre-temps. -
409
invalid_state_transition— Cette action n'est pas possible dans le statut actuel.
Changer le statut d’un bon d’intervention
/api/v1/work-orders/{work_order}/status
Passe le bon d’intervention en brouillon, planifié, en cours, terminé ou annulé. Pour en cours et terminé, started_at et completed_at sont renseignés. Comme dans l’application, terminer peut envoyer une demande d’avis au client si l’entreprise l’a configuré. Seul Klantly passe un bon au statut facturé.
- Scope
-
work_orders.write— Créer et modifier des bons d'intervention, et changer leur statut (terminer peut envoyer une demande d'avis) - Fonctionnalité requise
work_orders
Envoyez une Idempotency-Key : une nouvelle tentative après un délai d'attente ne crée alors jamais de doublon.
Paramètres de chemin
| Nom | Type | Description |
|---|---|---|
work_order obligatoire |
string (uuid) | L’id (UUID) du bon d’intervention. |
Corps (JSON)
| Champ | Type | Description |
|---|---|---|
status
obligatoire
|
string | draft (brouillon), planned (planifié), in_progress (en cours), completed (terminé), invoiced (facturé) ou cancelled (annulé). À la création : draft ou planned. l'une des valeurs : draft, planned, in_progress, completed, cancelled |
Exemple de requête
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"]Réponse 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"
}
}Erreurs possibles
-
403
insufficient_scope— Cette clé API n'a pas accès à cette action. -
422
validation_failed— Les données saisies ne sont pas valides. -
422
unknown_field— Les données contiennent un champ inconnu. -
415
unsupported_media_type— Ce format n'est pas pris en charge. -
413
payload_too_large— Le corps de la requête est trop volumineux. -
404
not_found— Introuvable. -
409
invalid_state_transition— Cette action n'est pas possible dans le statut actuel. -
422
idempotency_key_reused— Cette Idempotency-Key a déjà été utilisée pour une autre requête. -
409
idempotency_in_progress— Une requête avec cette Idempotency-Key est encore en cours.
Supprimer un bon d’intervention
/api/v1/work-orders/{work_order}
Supprime le bon d’intervention. Un bon d’intervention facturé ne peut pas être supprimé.
- Scope
-
work_orders.delete— Supprimer des bons d'intervention - Fonctionnalité requise
work_orders
Paramètres de chemin
| Nom | Type | Description |
|---|---|---|
work_order obligatoire |
string (uuid) | L’id (UUID) du bon d’intervention. |
Exemple de requête
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"]Réponse 200
{
"data": {
"object": "note",
"id": "9d3f7b41-2d6f-4e8c-9b3a-4f5d6e7a8b92",
"deleted": true
}
}Erreurs possibles
-
403
insufficient_scope— Cette clé API n'a pas accès à cette action. -
404
not_found— Introuvable. -
409
invalid_state_transition— Cette action n'est pas possible dans le statut actuel.
L'objet
Tous les champs sont toujours présents ; un champ sans valeur vaut null.
| Champ | Type | Description |
|---|---|---|
object |
string | Toujours « work_order ». |
id |
string (uuid) | Id unique (UUID). |
number |
string | Numéro du bon, par exemple WB-2026-00042 ; attribué par Klantly. |
status |
string | draft (brouillon), planned (planifié), in_progress (en cours), completed (terminé), invoiced (facturé) ou cancelled (annulé). À la création : draft ou planned. l'une des valeurs : draft, planned, in_progress, completed, invoiced, cancelled |
title |
string | Titre. peut être vide (null) |
type |
string | Type de travail, par exemple installation, réparation ou entretien. peut être vide (null) |
location |
string | Lieu de l’intervention, s’il ne s’agit pas de l’adresse du client. peut être vide (null) |
description |
string | Description du travail ou de la réclamation. peut être vide (null) |
work_performed |
string | Ce qui a été fait. peut être vide (null) |
customer_notes |
string | Remarques pour le client ; figurent sur le bon. peut être vide (null) |
customer_id |
string (uuid) | Le client. Obligatoire à la création ; le nom, l’adresse et les coordonnées viennent du client. peut être vide (null) |
customer |
object | Les données du client sur le bon, telles qu’elles étaient à la création. |
customer.type |
string | individual (particulier) ou business (entreprise). peut être vide (null) |
customer.name |
string | Nom ; pour une entreprise, la raison sociale. peut être vide (null) |
customer.email |
string | Adresse e-mail. peut être vide (null) |
customer.phone |
string | Numéro de téléphone. peut être vide (null) |
customer.address |
string | Rue et numéro. peut être vide (null) |
customer.postal_code |
string | Code postal. peut être vide (null) |
customer.city |
string | Ville. peut être vide (null) |
customer.country |
string | Pays. peut être vide (null) |
deal_id |
string (uuid) | Le deal du tableau pipeline, ou null. peut être vide (null) |
appointment_id |
string (uuid) | Le rendez-vous auquel le bon appartient, ou null. peut être vide (null) |
quote_id |
string (uuid) | Le devis dont provient le bon, ou null. peut être vide (null) |
invoice_id |
string (uuid) | La facture du bon, ou null. peut être vide (null) |
assigned_user_id |
string | Le technicien ou l’utilisateur qui réalise le travail, ou null. peut être vide (null) |
language |
string | Langue du bon : nl, en, de ou fr. l'une des valeurs : nl, en, de, fr |
currency |
string | Toujours « EUR ». |
subtotal |
string | Total hors TVA, sous forme de texte avec deux décimales. |
tax_amount |
string | Montant de la TVA. |
total |
string | Total TVA comprise. |
scheduled_at |
string (date-time) | Date prévue de l’intervention (UTC). En entrée : ISO 8601 avec fuseau horaire. peut être vide (null) |
started_at |
string (date-time) | Début de l’intervention. peut être vide (null) |
completed_at |
string (date-time) | Fin de l’intervention. peut être vide (null) |
sent_at |
string (date-time) | Date d’envoi du bon au client. peut être vide (null) |
signature |
object | La signature du client (données uniquement, pas d’image). |
signature.is_signed |
boolean | Indique si le bon est signé. |
signature.signed_by_name |
string | Nom du signataire. peut être vide (null) |
signature.signed_at |
string (date-time) | Date de la signature. peut être vide (null) |
items |
array<object> | Les lignes (200 au maximum). En entrée : une liste avec par ligne name (obligatoire) et en option type, description, sku, quantity, unit, unit_price, discount_percentage, discount_amount, tax_rate, is_taxable et minutes ; la liste remplace toutes les lignes. |
items.object |
string | Toujours « work_order_item ». |
items.id |
string (uuid) | Id de la ligne. Change à chaque envoi de items (les lignes sont remplacées). |
items.type |
string | labor (main-d’œuvre), material (matériel) ou other. l'une des valeurs : labor, material, other |
items.name |
string | Libellé sur le bon. |
items.description |
string | Précision. peut être vide (null) |
items.sku |
string | Référence article. peut être vide (null) |
items.quantity |
string | Quantité. |
items.unit |
string | Unité, par exemple heure ou pièce. peut être vide (null) |
items.unit_price |
string | Prix unitaire hors TVA. |
items.discount_percentage |
string | Remise en pourcentage. |
items.discount_amount |
string | Remise fixe sur la ligne hors TVA ; ne compte que si discount_percentage vaut 0. |
items.line_total |
string | Total de la ligne hors TVA ; calculé par Klantly. |
items.is_taxable |
boolean | Indique si la ligne est soumise à la TVA. |
items.tax_rate |
string | Taux de TVA en pourcentage (21 par défaut, même si l’entreprise applique un autre taux : envoyez-le alors). |
items.minutes |
integer | Minutes travaillées, pour la main-d’œuvre. peut être vide (null) |
checklist |
array<object> | La check-list (200 points au maximum). En entrée : par point label (obligatoire) et en option checked, required et note ; la liste remplace toute la check-list. |
checklist.object |
string | Toujours « work_order_checklist_item ». |
checklist.id |
string (uuid) | Id du point. |
checklist.label |
string | Le point, par exemple « Pression contrôlée ». |
checklist.checked |
boolean | Coché. |
checklist.required |
boolean | Doit obligatoirement être coché. |
checklist.note |
string | Remarque sur le point. peut être vide (null) |
photos |
array<object> | Les photos du bon (données uniquement ; les fichiers ne sont pas encore dans l’API). |
photos.object |
string | Toujours « work_order_photo ». |
photos.id |
string (uuid) | Id de la photo. |
photos.kind |
string | before (avant), after (après) ou other. peut être vide (null) · l'une des valeurs : before, after, other |
photos.caption |
string | Légende. peut être vide (null) |
photos.created_at |
string (date-time) | Téléversée le (UTC). |
created_at |
string (date-time) | Créé le (UTC). |
updated_at |
string (date-time) | Dernière modification (UTC). |