Référence de l'API
Endpoints de webhook
Des adresses auxquelles Klantly envoie immédiatement un message dès qu'un changement a lieu. Les signatures et les nouvelles tentatives sont expliquées dans le guide des webhooks.
Endpoints
-
GET
/webhook-endpointsLister les endpoints de webhook -
GET
/webhook-endpoints/{endpoint}Récupérer un endpoint de webhook -
POST
/webhook-endpointsCréer un endpoint de webhook -
PATCH
/webhook-endpoints/{endpoint}Modifier un endpoint de webhook -
DELETE
/webhook-endpoints/{endpoint}Supprimer un endpoint de webhook -
POST
/webhook-endpoints/{endpoint}/testEnvoyer un message de test -
POST
/webhook-endpoints/{endpoint}/rotate-secretRenouveler le secret
Lister les endpoints de webhook
/api/v1/webhook-endpoints
Tous les endpoints de webhook de votre entreprise, le plus ancien en premier. Le secret n'y figure jamais.
- Scope
-
webhooks.manage— Gérer les endpoints de webhook
Exemple de requête
curl "https://app.klantly.com/api/v1/webhook-endpoints" \
-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', 'webhook-endpoints');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/webhook-endpoints', {
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/webhook-endpoints",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
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": "webhook_endpoint",
"id": "01j7zr8m2k4n6p8r0t2v4w6y8a",
"url": "https://example.com/webhooks/klantly",
"description": "Boekhouding",
"events": [
"customer.created",
"deal.won"
],
"status": "active",
"disabled_reason": null,
"disabled_at": null,
"previous_secret_expires_at": null,
"last_success_at": "2026-09-14T10:15:02Z",
"last_failure_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
],
"meta": {
"limit": 50,
"next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
"prev_cursor": null
}
}Erreurs possibles
-
403
insufficient_scope— Cette clé API n'a pas accès à cette action.
Récupérer un endpoint de webhook
/api/v1/webhook-endpoints/{endpoint}
Un endpoint par id, avec son statut et l'heure de la dernière livraison réussie et de la dernière livraison échouée.
- Scope
-
webhooks.manage— Gérer les endpoints de webhook
Paramètres de chemin
| Nom | Type | Description |
|---|---|---|
endpoint obligatoire |
string | L'id de l'endpoint de webhook. |
Exemple de requête
curl "https://app.klantly.com/api/v1/webhook-endpoints/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', 'webhook-endpoints/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/webhook-endpoints/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/webhook-endpoints/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Réponse 200
{
"data": {
"object": "webhook_endpoint",
"id": "01j7zr8m2k4n6p8r0t2v4w6y8a",
"url": "https://example.com/webhooks/klantly",
"description": "Boekhouding",
"events": [
"customer.created",
"deal.won"
],
"status": "active",
"disabled_reason": null,
"disabled_at": null,
"previous_secret_expires_at": null,
"last_success_at": "2026-09-14T10:15:02Z",
"last_failure_at": null,
"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 endpoint de webhook
/api/v1/webhook-endpoints
Enregistre une adresse https qui reçoit les événements. La réponse contient le secret pour vérifier la signature : conservez-le immédiatement, vous ne le reverrez plus. Vous ne pouvez choisir que des événements portant sur des données que votre clé peut lire ; * signifie tous ces événements, y compris les futurs. 10 endpoints au maximum par entreprise.
- Scope
-
webhooks.manage— Gérer les endpoints de webhook
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 |
|---|---|---|
url
obligatoire
|
string (uri) | L'adresse https à laquelle Klantly envoie les événements. Pas d'adresses internes, et uniquement les ports 443, 80 ou 8443. au maximum 2048 caractères |
description
facultatif
|
string | Votre propre description, par exemple à quoi sert l'endpoint. peut être vide (null) · au maximum 255 caractères |
events
obligatoire
|
array | Les types d'événements que cet endpoint reçoit, ou ["*"] pour tous les événements que la clé ou l'utilisateur peut lire. |
Exemple de requête
curl -X POST "https://app.klantly.com/api/v1/webhook-endpoints" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f" \
-d '{
"url": "https://example.com/webhooks/klantly",
"description": "Boekhouding",
"events": [
"customer.created",
"deal.won"
]
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'webhook-endpoints', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'url' => 'https://example.com/webhooks/klantly',
'description' => 'Boekhouding',
'events' => [
0 => 'customer.created',
1 => 'deal.won',
],
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/webhook-endpoints', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
body: JSON.stringify({
"url": "https://example.com/webhooks/klantly",
"description": "Boekhouding",
"events": [
"customer.created",
"deal.won"
]
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/webhook-endpoints",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
json={
"url": "https://example.com/webhooks/klantly",
"description": "Boekhouding",
"events": [
"customer.created",
"deal.won"
]
},
)
data = response.json()["data"]Réponse 201
{
"data": {
"object": "webhook_endpoint",
"id": "01j7zr8m2k4n6p8r0t2v4w6y8a",
"url": "https://example.com/webhooks/klantly",
"description": "Boekhouding",
"events": [
"customer.created",
"deal.won"
],
"status": "active",
"disabled_reason": null,
"disabled_at": null,
"previous_secret_expires_at": null,
"last_success_at": "2026-09-14T10:15:02Z",
"last_failure_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z",
"secret": "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw"
}
}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. -
403
limit_reached— La limite de l'abonnement est atteinte. -
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 endpoint de webhook
/api/v1/webhook-endpoints/{endpoint}
Modifie uniquement les champs que vous envoyez. Avec status, vous désactivez ou réactivez l’endpoint ; la réactivation remet le compteur d’erreurs à zéro. Celui qui choisit à nouveau les événements détermine ce que l’endpoint peut voir : jamais plus que cette clé. Toute modification (y compris la désactivation ou une autre url) nécessite une clé qui peut lire tout ce que l’endpoint reçoit actuellement ; sinon, vous recevez une 403 insufficient_scope.
- Scope
-
webhooks.manage— Gérer les endpoints de webhook
Envoyez l'ETag dans If-Match : vous n'écraserez jamais par erreur une version plus récente.
Paramètres de chemin
| Nom | Type | Description |
|---|---|---|
endpoint obligatoire |
string | L'id de l'endpoint de webhook. |
Corps (JSON)
| Champ | Type | Description |
|---|---|---|
url
facultatif
|
string (uri) | L'adresse https à laquelle Klantly envoie les événements. Pas d'adresses internes, et uniquement les ports 443, 80 ou 8443. au maximum 2048 caractères |
description
facultatif
|
string | Votre propre description, par exemple à quoi sert l'endpoint. peut être vide (null) · au maximum 255 caractères |
events
facultatif
|
array | Les types d'événements que cet endpoint reçoit, ou ["*"] pour tous les événements que la clé ou l'utilisateur peut lire. |
status
facultatif
|
string | active ou disabled (désactivé). l'une des valeurs : active, disabled |
Exemple de requête
curl -X PATCH "https://app.klantly.com/api/v1/webhook-endpoints/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": [
"*"
],
"status": "active"
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('PATCH', 'webhook-endpoints/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', [
'json' => [
'events' => [
0 => '*',
],
'status' => 'active',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/webhook-endpoints/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"events": [
"*"
],
"status": "active"
}),
});
const { data } = await response.json();import os
import requests
response = requests.patch(
"https://app.klantly.com/api/v1/webhook-endpoints/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
json={
"events": [
"*"
],
"status": "active"
},
)
data = response.json()["data"]Réponse 200
{
"data": {
"object": "webhook_endpoint",
"id": "01j7zr8m2k4n6p8r0t2v4w6y8a",
"url": "https://example.com/webhooks/klantly",
"description": "Boekhouding",
"events": [
"customer.created",
"deal.won"
],
"status": "active",
"disabled_reason": null,
"disabled_at": null,
"previous_secret_expires_at": null,
"last_success_at": "2026-09-14T10:15:02Z",
"last_failure_at": null,
"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.
Supprimer un endpoint de webhook
/api/v1/webhook-endpoints/{endpoint}
Supprime l'endpoint. Les événements encore en cours d'envoi ne sont plus livrés. Nécessite une clé qui peut lire tout ce que l’endpoint reçoit.
- Scope
-
webhooks.manage— Gérer les endpoints de webhook
Paramètres de chemin
| Nom | Type | Description |
|---|---|---|
endpoint obligatoire |
string | L'id de l'endpoint de webhook. |
Exemple de requête
curl -X DELETE "https://app.klantly.com/api/v1/webhook-endpoints/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', 'webhook-endpoints/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/webhook-endpoints/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/webhook-endpoints/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.
Envoyer un message de test
/api/v1/webhook-endpoints/{endpoint}/test
Envoie immédiatement l'événement ping à l'endpoint, même s'il est désactivé, et renvoie la tentative de livraison. Un message de test n'est pas renvoyé en cas d'échec.
- Scope
-
webhooks.manage— Gérer les endpoints de webhook
Paramètres de chemin
| Nom | Type | Description |
|---|---|---|
endpoint obligatoire |
string | L'id de l'endpoint de webhook. |
Exemple de requête
curl -X POST "https://app.klantly.com/api/v1/webhook-endpoints/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/test" \
-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('POST', 'webhook-endpoints/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/test');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/webhook-endpoints/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/test', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/webhook-endpoints/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/test",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Réponse 200
{
"data": {
"object": "webhook_delivery",
"id": "whd_0p5t7j31a2e4gm",
"webhook_endpoint_id": "01j7zr8m2k4n6p8r0t2v4w6y8a",
"event_id": "evt_01j7zs1a2b3c4d5e6f7g8h9j0k",
"event_type": "ping",
"attempt": 1,
"status": "succeeded",
"response_status": 200,
"error": null,
"duration_ms": 184,
"created_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.
Renouveler le secret
/api/v1/webhook-endpoints/{endpoint}/rotate-secret
Crée un nouveau secret et le renvoie une seule fois. Pendant le chevauchement, Klantly signe avec l’ancien et le nouveau secret, pour que votre récepteur puisse basculer sans interruption. Nécessite une clé qui peut lire tout ce que l’endpoint reçoit ; sinon, vous recevez une 403 insufficient_scope.
- Scope
-
webhooks.manage— Gérer les endpoints de webhook
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 |
|---|---|---|
endpoint obligatoire |
string | L'id de l'endpoint de webhook. |
Corps (JSON)
| Champ | Type | Description |
|---|---|---|
overlap_hours
facultatif
|
integer | Combien de temps l'ancien secret reste valable à côté du nouveau : 0, 1, 24 (par défaut) ou 168 heures. l'une des valeurs : 0, 1, 24, 168 |
Exemple de requête
curl -X POST "https://app.klantly.com/api/v1/webhook-endpoints/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/rotate-secret" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f" \
-d '{
"overlap_hours": 24
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'webhook-endpoints/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/rotate-secret', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'overlap_hours' => 24,
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/webhook-endpoints/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/rotate-secret', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
body: JSON.stringify({
"overlap_hours": 24
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/webhook-endpoints/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/rotate-secret",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
json={
"overlap_hours": 24
},
)
data = response.json()["data"]Réponse 200
{
"data": {
"object": "webhook_endpoint",
"id": "01j7zr8m2k4n6p8r0t2v4w6y8a",
"url": "https://example.com/webhooks/klantly",
"description": "Boekhouding",
"events": [
"customer.created",
"deal.won"
],
"status": "active",
"disabled_reason": null,
"disabled_at": null,
"previous_secret_expires_at": null,
"last_success_at": "2026-09-14T10:15:02Z",
"last_failure_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z",
"secret": "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw"
}
}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. -
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.
L'objet
Tous les champs sont toujours présents ; un champ sans valeur vaut null.
| Champ | Type | Description |
|---|---|---|
object |
string | Toujours « webhook_endpoint ». |
id |
string | Id de l'endpoint. |
url |
string (uri) | L'adresse https à laquelle Klantly envoie les événements. Pas d'adresses internes, et uniquement les ports 443, 80 ou 8443. |
description |
string | Votre propre description, par exemple à quoi sert l'endpoint. peut être vide (null) |
events |
array<string> | Les types d'événements que cet endpoint reçoit, ou ["*"] pour tous les événements que la clé ou l'utilisateur peut lire. |
status |
string | active ou disabled (désactivé). l'une des valeurs : active, disabled |
disabled_reason |
string | Pourquoi l’endpoint est désactivé : failing (uniquement des erreurs pendant 5 jours), manual (à la main) ou key_revoked (la clé API qui l’a configuré a été révoquée), sinon null. peut être vide (null) · l'une des valeurs : failing, manual, key_revoked |
disabled_at |
string (date-time) | Quand l'endpoint a été désactivé. peut être vide (null) |
previous_secret_expires_at |
string (date-time) | Jusqu'à quand l'ancien secret est encore utilisé après un renouvellement, sinon null. peut être vide (null) |
last_success_at |
string (date-time) | Dernière livraison réussie. peut être vide (null) |
last_failure_at |
string (date-time) | Dernière livraison échouée. peut être vide (null) |
created_at |
string (date-time) | Créé le (UTC). |
updated_at |
string (date-time) | Dernière modification le (UTC). |