API reference
Events
Everything that changed in the last 30 days, as a list. To catch up on missed webhooks, or to poll instead of using webhooks.
Endpoints
List events
GET
/api/v1/events
The events of the last 30 days, newest first. You only see events about data your key can read. Use sort=created_at with filter[created_since] to catch up on missed events in order.
- Scope
-
events.read— Retrieve events
Query parameters
| Name | Type | Description |
|---|---|---|
limit |
integer | Number of results per page. from 1 to 100 · default: 50 |
cursor |
string | The next_cursor or prev_cursor from meta of the previous response. |
sort |
string | created_at keeps the order in which things happened, -created_at (default) shows the newest first. one of: -created_at, created_at · default: -created_at |
filter[type] |
string | Only these event types, separated by commas, for example customer.created,deal.won. at most 500 characters |
filter[created_since] |
string (date-time) | Only events from this moment on: ISO 8601 with a time zone, for example 2026-09-14T10:15:00Z. |
Example request
curl "https://app.klantly.com/api/v1/events?filter[type]=customer.created%2Cdeal.won&sort=created_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', 'events', [
'query' => [
'filter[type]' => 'customer.created,deal.won',
'sort' => 'created_at',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/events?filter[type]=customer.created%2Cdeal.won&sort=created_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/events",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
params={
"filter[type]": "customer.created,deal.won",
"sort": "created_at"
},
)
data = response.json()["data"]Response 200
The response is a list with cursor pagination: data contains the objects, meta the pagination.
{
"data": [
{
"object": "event",
"id": "evt_01j7zs1a2b3c4d5e6f7g8h9j0k",
"type": "customer.created",
"created_at": "2026-09-14T10:15:00Z",
"data": {
"object": {
"object": "customer",
"id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"type": "business",
"status": "lead",
"name": "Jan de Vries",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"company_name": "De Vries Bouw",
"vat_number": "NL123456789B01",
"coc_number": "12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"email_unsubscribed": false,
"converted_at": null,
"last_activity_at": "2026-09-14T10:15:00Z",
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}
}
],
"meta": {
"limit": 50,
"next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
"prev_cursor": null
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid.
Retrieve an event
GET
/api/v1/events/{event}
One event by id, for example the webhook-id of a message you received.
- Scope
-
events.read— Retrieve events
Path parameters
| Name | Type | Description |
|---|---|---|
event required |
string | The id of the event (evt_…). |
Example request
curl "https://app.klantly.com/api/v1/events/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', 'events/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/events/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/events/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "event",
"id": "evt_01j7zs1a2b3c4d5e6f7g8h9j0k",
"type": "customer.created",
"created_at": "2026-09-14T10:15:00Z",
"data": {
"object": {
"object": "customer",
"id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"type": "business",
"status": "lead",
"name": "Jan de Vries",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"company_name": "De Vries Bouw",
"vat_number": "NL123456789B01",
"coc_number": "12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"email_unsubscribed": false,
"converted_at": null,
"last_activity_at": "2026-09-14T10:15:00Z",
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found.
The object
All fields are always present; a field without a value is null.
| Field | Type | Description |
|---|---|---|
object |
string | Always "event". |
id |
string | Unique id (evt_…). Same as the webhook-id header: use it to recognise duplicate messages. |
type |
string | The kind of event, for example customer.created. one of: customer.created, customer.updated, customer.converted, deal.created, deal.updated, deal.stage_changed, deal.won, deal.lost, note.created, note.updated, note.deleted |
created_at |
string (date-time) | When it happened (UTC). |
data |
object | The content of the event. |
data.object |
object | The object as it was after the change, in the same shape as in the REST API (for note.deleted: as it was before deletion). |