API-referentie
Webhook-endpoints
Adressen waar Klantly direct een bericht naartoe stuurt zodra er iets verandert. Ondertekening en herhalingen staan uitgelegd in de gids over webhooks.
Endpoints
-
GET
/webhook-endpointsWebhook-endpoints opvragen -
GET
/webhook-endpoints/{endpoint}Webhook-endpoint ophalen -
POST
/webhook-endpointsWebhook-endpoint aanmaken -
PATCH
/webhook-endpoints/{endpoint}Webhook-endpoint bijwerken -
DELETE
/webhook-endpoints/{endpoint}Webhook-endpoint verwijderen -
POST
/webhook-endpoints/{endpoint}/testTestbericht versturen -
POST
/webhook-endpoints/{endpoint}/rotate-secretSecret vernieuwen
Webhook-endpoints opvragen
/api/v1/webhook-endpoints
Alle webhook-endpoints van je bedrijf, oudste eerst. Het secret staat er nooit in.
- Scope
-
webhooks.manage— Webhook-endpoints beheren
Voorbeeldverzoek
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"]Antwoord 200
Het antwoord is een lijst met cursorpaginering: data bevat de objecten, meta de paginering.
{
"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
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie.
Webhook-endpoint ophalen
/api/v1/webhook-endpoints/{endpoint}
Eén endpoint op id, met de status en het tijdstip van de laatste geslaagde en mislukte aflevering.
- Scope
-
webhooks.manage— Webhook-endpoints beheren
Padparameters
| Naam | Type | Omschrijving |
|---|---|---|
endpoint verplicht |
string | De id van het webhook-endpoint. |
Voorbeeldverzoek
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"]Antwoord 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"
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie. -
404
not_found— Niet gevonden.
Webhook-endpoint aanmaken
/api/v1/webhook-endpoints
Registreert een https-adres dat events ontvangt. Het antwoord bevat het secret om de handtekening te controleren: bewaar het direct, je ziet het hierna niet meer. Je kunt alleen events kiezen over gegevens waar je sleutel leesrecht op heeft; * betekent alle events die daaronder vallen, ook toekomstige. Maximaal 10 endpoints per bedrijf.
- Scope
-
webhooks.manage— Webhook-endpoints beheren
Stuur een Idempotency-Key mee, dan maakt een nieuwe poging na een time-out geen dubbel record.
Body (JSON)
| Veld | Type | Omschrijving |
|---|---|---|
url
verplicht
|
string (uri) | Het https-adres waar Klantly de events naartoe stuurt. Geen interne adressen, en alleen poort 443, 80 of 8443. maximaal 2048 tekens |
description
optioneel
|
string | Eigen omschrijving, bijvoorbeeld waar het endpoint voor dient. kan leeg zijn (null) · maximaal 255 tekens |
events
verplicht
|
array | De eventtypes die dit endpoint ontvangt, of ["*"] voor alle events waar de sleutel of gebruiker leesrecht op heeft. |
Voorbeeldverzoek
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"]Antwoord 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"
}
}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. -
403
limit_reached— De limiet van het abonnement is bereikt. -
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.
Webhook-endpoint bijwerken
/api/v1/webhook-endpoints/{endpoint}
Wijzigt alleen de velden die je meestuurt. Met status zet je het endpoint uit of weer aan; weer aanzetten zet de foutenteller op nul. Wie de events opnieuw kiest, bepaalt wat het endpoint mag zien: nooit meer dan die sleutel. Elke wijziging (ook uitzetten of een andere url) kan alleen met een sleutel die alles kan lezen wat het endpoint nu ontvangt; anders volgt 403 insufficient_scope.
- Scope
-
webhooks.manage— Webhook-endpoints beheren
Stuur de ETag mee in If-Match, dan overschrijf je nooit per ongeluk een nieuwere versie.
Padparameters
| Naam | Type | Omschrijving |
|---|---|---|
endpoint verplicht |
string | De id van het webhook-endpoint. |
Body (JSON)
| Veld | Type | Omschrijving |
|---|---|---|
url
optioneel
|
string (uri) | Het https-adres waar Klantly de events naartoe stuurt. Geen interne adressen, en alleen poort 443, 80 of 8443. maximaal 2048 tekens |
description
optioneel
|
string | Eigen omschrijving, bijvoorbeeld waar het endpoint voor dient. kan leeg zijn (null) · maximaal 255 tekens |
events
optioneel
|
array | De eventtypes die dit endpoint ontvangt, of ["*"] voor alle events waar de sleutel of gebruiker leesrecht op heeft. |
status
optioneel
|
string | active of disabled (uitgeschakeld). een van: active, disabled |
Voorbeeldverzoek
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"]Antwoord 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"
}
}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.
Webhook-endpoint verwijderen
/api/v1/webhook-endpoints/{endpoint}
Verwijdert het endpoint. Events die nog onderweg zijn, worden niet meer afgeleverd. Vraagt een sleutel die alles kan lezen wat het endpoint ontvangt.
- Scope
-
webhooks.manage— Webhook-endpoints beheren
Padparameters
| Naam | Type | Omschrijving |
|---|---|---|
endpoint verplicht |
string | De id van het webhook-endpoint. |
Voorbeeldverzoek
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"]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.
Testbericht versturen
/api/v1/webhook-endpoints/{endpoint}/test
Stuurt direct het event ping naar het endpoint, ook als het uitstaat, en geeft de afleverpoging terug. Een testbericht wordt niet herhaald.
- Scope
-
webhooks.manage— Webhook-endpoints beheren
Padparameters
| Naam | Type | Omschrijving |
|---|---|---|
endpoint verplicht |
string | De id van het webhook-endpoint. |
Voorbeeldverzoek
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"]Antwoord 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"
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie. -
404
not_found— Niet gevonden.
Secret vernieuwen
/api/v1/webhook-endpoints/{endpoint}/rotate-secret
Maakt een nieuw secret aan en geeft het eenmalig terug. Tijdens de overlap ondertekent Klantly met het oude én het nieuwe secret, zodat je ontvanger zonder onderbreking kan overstappen. Vraagt een sleutel die alles kan lezen wat het endpoint ontvangt; anders volgt 403 insufficient_scope.
- Scope
-
webhooks.manage— Webhook-endpoints beheren
Stuur een Idempotency-Key mee, dan maakt een nieuwe poging na een time-out geen dubbel record.
Padparameters
| Naam | Type | Omschrijving |
|---|---|---|
endpoint verplicht |
string | De id van het webhook-endpoint. |
Body (JSON)
| Veld | Type | Omschrijving |
|---|---|---|
overlap_hours
optioneel
|
integer | Hoe lang het oude secret naast het nieuwe blijft werken: 0, 1, 24 (standaard) of 168 uur. een van: 0, 1, 24, 168 |
Voorbeeldverzoek
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"]Antwoord 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"
}
}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. -
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.
Het object
Alle velden zijn altijd aanwezig; een veld zonder waarde is null.
| Veld | Type | Omschrijving |
|---|---|---|
object |
string | Altijd "webhook_endpoint". |
id |
string | Id van het endpoint. |
url |
string (uri) | Het https-adres waar Klantly de events naartoe stuurt. Geen interne adressen, en alleen poort 443, 80 of 8443. |
description |
string | Eigen omschrijving, bijvoorbeeld waar het endpoint voor dient. kan leeg zijn (null) |
events |
array<string> | De eventtypes die dit endpoint ontvangt, of ["*"] voor alle events waar de sleutel of gebruiker leesrecht op heeft. |
status |
string | active of disabled (uitgeschakeld). een van: active, disabled |
disabled_reason |
string | Waarom het endpoint uitstaat: failing (5 dagen alleen fouten), manual (handmatig) of key_revoked (de API-sleutel die het inrichtte is ingetrokken), anders null. kan leeg zijn (null) · een van: failing, manual, key_revoked |
disabled_at |
string (date-time) | Wanneer het endpoint werd uitgeschakeld. kan leeg zijn (null) |
previous_secret_expires_at |
string (date-time) | Tot wanneer na een vernieuwing ook het vorige secret nog wordt gebruikt, anders null. kan leeg zijn (null) |
last_success_at |
string (date-time) | Laatste geslaagde aflevering. kan leeg zijn (null) |
last_failure_at |
string (date-time) | Laatste mislukte aflevering. kan leeg zijn (null) |
created_at |
string (date-time) | Aangemaakt op (UTC). |
updated_at |
string (date-time) | Laatst gewijzigd op (UTC). |