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.
- 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.
- 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": "1834",
"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.
- 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) ou manual (à la main), sinon null. peut être vide (null) · l'une des valeurs : failing, manual |
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). |