Events
The same envelopes Kleos POSTs to your webhook endpoints, readable on demand. Delivery and history are separate on purpose: an endpoint that was down, misconfigured, or not yet created has cost you nothing, because the events are still here.
Event types
| Type | When |
|---|---|
call.completed | A call reached a final state. Carries the outcome, duration and the appointment id if one was booked. |
appointment.booked | One of your customers got a meeting. Carries the appointment, including the timezone it was agreed in. |
customer.ready | A customer passed every condition for live calling. Nothing is now waiting on you or on Kleos. |
balance.low | Your spendable balance crossed 25%, 10% or zero. At zero, no new call starts. |
key.rotated | An API key was rotated. The previous secret keeps working until the grace window closes. |
number.assigned | A number or caller ID was rented for one of your customers. Carries the number itself and its monthly rent. |
number.payment_due | A rented number's rent could not be taken, or will not be affordable at the next renewal. Carries `release_due_on` once the countdown has started — the day the number is lost for good. |
number.released | A rented number was given back. This is permanent: the number is gone and cannot be recovered. |
website.ready | A website you ordered has been built and is ready for you to look at before it goes live. |
website.live | A website is live. Carries its address, and hosting starts billing from this date. |
website.failed | A website build could not be completed. The build fee has been refunded in full. |
website.hosting_due | A live site's hosting could not be taken, or will not be affordable at the next renewal. Carries `suspend_due_on` once the countdown has started — the day the site goes offline. |
website.suspended | A site is offline for unpaid hosting. It is NOT deleted — carries `purge_due_on`, the day it stops being recoverable. |
More will be added. Ignore a type you do not recognise rather than erroring on it.
The event stream, readable on demand
GET/v1/events
The same envelopes Kleos POSTs to your webhook endpoints, in reverse order of when they happened. This exists so a webhook is never the only copy: an endpoint that was down, misconfigured, or not yet created still has its events here. Newest first, so recovering from an outage means reading forward from the last id you processed. An unrecognised `type` is ignored rather than returning an empty list.
Permission: events:read
Parameters
| Name | In | Type | |
|---|---|---|---|
type | query | string | One event type, e.g. `call.completed`. |
customer_id | query | string | Only events about this customer. |
limit | query | integer | How many to return. Defaults to 100, capped at 200. |
Response
{
"object": "list",
"data": [
{
"id": "evt_2f7c1a90b3e64d8a51c07e9f",
"object": "event",
"type": "call.completed",
"mode": "live",
"created_at": "2026-08-04T10:06:38.000Z",
"data": {
"object": "call",
"id": "9f31c0aa-6d1b-4f2d-8a6b-71c0e2d94f13",
"customer_id": "0f2a6c4e-96a2-4f2e-8f2b-6c1a0e73d8b1",
"status": "completed",
"finished": true,
"outcome": "appointment_booked",
"duration_seconds": 267,
"appointment_id": "b71f0c2e-2a53-4a10-9d3f-5c0b7e1a9d44"
}
}
],
"has_more": false
}One event
GET/v1/events/{id}
Resolves the `id` from a webhook envelope. Read it back rather than trusting a body you could not verify — a POST whose signature failed is a POST you should refuse, and this is how you then find out what it said. An event that is not yours returns the same 404 as one that does not exist.
Permission: events:read
Parameters
| Name | In | Type | |
|---|---|---|---|
id * | path | string | The event id, e.g. `evt_2f7c1a90b3e64d8a51c07e9f`. |
Response
{
"id": "evt_2f7c1a90b3e64d8a51c07e9f",
"object": "event",
"type": "appointment.booked",
"mode": "live",
"created_at": "2026-08-04T10:06:38.000Z",
"data": {
"object": "appointment",
"id": "b71f0c2e-2a53-4a10-9d3f-5c0b7e1a9d44",
"customer_id": "0f2a6c4e-96a2-4f2e-8f2b-6c1a0e73d8b1",
"call_id": "9f31c0aa-6d1b-4f2d-8a6b-71c0e2d94f13",
"start_at": "2026-08-06T13:30:00.000Z",
"end_at": "2026-08-06T13:50:00.000Z",
"timezone": "Europe/Brussels",
"status": "booked"
}
}Refusals
| Status | Code | When |
|---|---|---|
| 404 | event_not_found | No event of yours matches that id in this mode. |