Authentication
Use the API key issued to your organization. It starts with kc_live_ and grants access to the whole organization: treat it like a password. Keys already issued keep working; new keys can no longer be created.
Send it on every request:
curl https://book.kombineo.com/api/v1/me \ -H "Authorization: Bearer kc_live_…"
A missing or unknown key answers 401 unauthorized; a revoked key answers 401 key_revoked. The key is never accepted in the query string.
Errors
Every error has the same shape, with a stable code and a human-readable message:
{
"error": {
"code": "validation_error",
"message": "min_start_time must be an ISO 8601 date-time."
}
}400 validation_error— a parameter or a body field is malformed. The message names it.401 unauthorized/key_revoked— see Authentication.403 plan_required— the API (and Zapier, Make) is part of the SCALING plan and above. Keys stay in place and work again as soon as the organization is back on SCALING.404 not_found— the resource does not exist in your organization. A malformed identifier is also a 404.409 conflict— the action is not possible in the current state (recording a result before the meeting started, for instance).503 not_configured— the API is not enabled on this environment.
Pagination
List endpoints take limit (1–100, default 20) and offset, and return a pagination block. Follow next_offset until has_more is false.
{
"limit": 20,
"offset": 40,
"count": 20,
"total": 143,
"has_more": true,
"next_offset": 60
}Dates are ISO 8601 in UTC. Identifiers are UUIDs. Every object carries an object field naming its type.
Account
Who am I.
Identify the caller
Returns the organization and the API key behind the request. Use it to test a connection.
Response 200
{
"object": "me",
"organization": {
"id": "0b2c1d4e-5f60-4a7b-8c9d-0e1f2a3b4c5d",
"slug": "synergie",
"name": "Synergie",
"plan": "scaling",
"plan_status": "active"
},
"api_key": {
"id": "d2f6a1b3-5c7e-4f90-a1b2-c3d4e5f60718",
"name": "Zapier",
"prefix": "kc_live_Ab12",
"created_at": "2026-09-12T09:00:00.000Z"
}
}401 — Missing, unknown or revoked API key.
Funnels
Your booking pages and their calendars.
List funnels
Every booking page of the organization except archived ones, with their calendars, assigned members and published questions.
Response 200
{
"data": [
{
"id": "a1d4c8b2-6f39-4e07-9c15-3b8d2e6a4c71",
"object": "funnel",
"slug": "appel-decouverte",
"name": "Appel découverte",
"status": "published",
"url": "https://kalleo.io/synergie/appel-decouverte",
"duration_minutes": 45,
"scoring_active": true,
"questions": [
{
"id": "budget",
"type": "single-choice",
"label": "Quel est votre budget ?"
},
{
"id": "decision",
"type": "single-choice",
"label": "Êtes-vous décisionnaire ?"
}
],
"calendars": [
{
"id": "e6b2f7a3-1c94-4d80-a5e2-7f3c9b1d0428",
"key": "hot",
"name": "Séniors uniquement",
"tier": "hot",
"duration_minutes": 45,
"split_percent": null,
"members": [
{
"id": "4e8c5703-265c-45b2-9e76-9deeedb65007",
"name": "Malo Durand",
"email": "malo@example.com",
"priority": "max",
"location": "meet"
}
]
}
],
"published_at": "2026-09-01T08:00:00.000Z",
"created_at": "2026-08-20T10:00:00.000Z"
}
]
}401 — Missing, unknown or revoked API key.
Get a funnel
One booking page by identifier.
| Parameter | In | Description |
|---|---|---|
| id* | path | Funnel identifier (UUID). |
Response 200
{
"id": "a1d4c8b2-6f39-4e07-9c15-3b8d2e6a4c71",
"object": "funnel",
"slug": "appel-decouverte",
"name": "Appel découverte",
"status": "published",
"url": "https://kalleo.io/synergie/appel-decouverte",
"duration_minutes": 45,
"scoring_active": true,
"questions": [
{
"id": "budget",
"type": "single-choice",
"label": "Quel est votre budget ?"
},
{
"id": "decision",
"type": "single-choice",
"label": "Êtes-vous décisionnaire ?"
}
],
"calendars": [
{
"id": "e6b2f7a3-1c94-4d80-a5e2-7f3c9b1d0428",
"key": "hot",
"name": "Séniors uniquement",
"tier": "hot",
"duration_minutes": 45,
"split_percent": null,
"members": [
{
"id": "4e8c5703-265c-45b2-9e76-9deeedb65007",
"name": "Malo Durand",
"email": "malo@example.com",
"priority": "max",
"location": "meet"
}
]
}
],
"published_at": "2026-09-01T08:00:00.000Z",
"created_at": "2026-08-20T10:00:00.000Z"
}401 — Missing, unknown or revoked API key.
404 — Not found in your organization.
Members
The people of your organization.
List members
Administrators and closers of the organization.
Response 200
{
"data": [
{
"id": "4e8c5703-265c-45b2-9e76-9deeedb65007",
"object": "member",
"name": "Malo Durand",
"email": "malo@example.com",
"role": "member",
"status": "active",
"timezone": "Europe/Paris",
"created_at": "2026-08-20T10:00:00.000Z"
}
]
}401 — Missing, unknown or revoked API key.
Bookings
Meetings booked by prospects.
List bookings
Meetings of the organization, newest start first by default. Combine filters freely; dates are ISO 8601.
| Parameter | In | Description |
|---|---|---|
| status | query | Comma-separated list among `confirmed`, `cancelled`, `completed`, `no_show`. |
| funnel_id | query | Only meetings booked on this funnel. |
| member_id | query | Only meetings assigned to this closer. |
| calendar_id | query | Only meetings on this calendar. |
| lead_id | query | Only meetings of this lead. |
| invitee_email | query | Exact invitee email (case-insensitive). |
| min_start_time | query | Meetings starting at or after this instant. |
| max_start_time | query | Meetings starting at or before this instant. |
| created_after | query | Meetings booked at or after this instant. |
| created_before | query | Meetings booked at or before this instant. |
| sort | query | `start_time:desc` (default), `start_time:asc`, `created_at:desc`, `created_at:asc`. |
| limit | query | Page size, 1–100. Defaults to 20. |
| offset | query | Number of items to skip. Use `pagination.next_offset` from the previous page. |
Response 200
{
"data": [
{
"id": "3a7d51e0-08c6-4a3b-9c1d-6f2b5d8e4a90",
"object": "booking",
"status": "confirmed",
"outcome": null,
"start_time": "2026-09-15T12:00:00.000Z",
"end_time": "2026-09-15T12:45:00.000Z",
"duration_minutes": 45,
"location": "meet",
"meeting_url": "https://meet.google.com/abc-defg-hij",
"invitee": {
"name": "Camille Dubois",
"email": "camille.dubois@example.com",
"phone": "+33612345678",
"timezone": "Europe/Paris"
},
"host": {
"id": "4e8c5703-265c-45b2-9e76-9deeedb65007",
"name": "Malo Durand",
"email": "malo@example.com",
"timezone": "Europe/Paris"
},
"funnel": {
"id": "a1d4c8b2-6f39-4e07-9c15-3b8d2e6a4c71",
"slug": "appel-decouverte",
"name": "Appel découverte",
"url": "https://kalleo.io/synergie/appel-decouverte"
},
"calendar": {
"id": "e6b2f7a3-1c94-4d80-a5e2-7f3c9b1d0428",
"key": "hot",
"name": "Séniors uniquement"
},
"lead": {
"id": "9f1c2d64-4b1e-4a1f-9a5e-1f0b7f3c2a11",
"status": "booked",
"score": 4.2,
"tier": "hot",
"country": "FR",
"source_code": "AB58935",
"link": {
"code": "AB58935",
"name": "rentree-2026",
"url": "https://synergie.example/offre?kalleo=AB58935",
"landing_page": {
"name": "synergie.example/offre",
"url": "https://synergie.example/offre"
}
},
"source": {
"name": "rentree-2026",
"platform": "meta",
"format": "video-1"
},
"utm": {
"source": "meta",
"medium": "paid_social",
"campaign": "rentree-2026",
"content": "video-1",
"term": null
},
"answers": [
{
"question_id": "budget",
"question": "Quel est votre budget ?",
"value": "more-5k",
"label": "Plus de 5 000 €"
},
{
"question_id": "decision",
"question": "Êtes-vous décisionnaire ?",
"value": "yes",
"label": "Oui"
}
]
},
"notes": "Disponible surtout le matin.",
"manage_url": "https://kalleo.io/rdv/eyJhbGciOiJIUzI1NiJ9.example",
"created_at": "2026-09-12T09:31:04.000Z"
}
],
"pagination": {
"limit": 20,
"offset": 0,
"count": 1,
"total": 1,
"has_more": false,
"next_offset": null
}
}400 — A filter is malformed. The message names it.
401 — Missing, unknown or revoked API key.
Get a booking
One meeting with its invitee, host, funnel, calendar and lead (score, tier, form answers).
| Parameter | In | Description |
|---|---|---|
| id* | path | Booking identifier (UUID). |
Response 200
{
"id": "3a7d51e0-08c6-4a3b-9c1d-6f2b5d8e4a90",
"object": "booking",
"status": "confirmed",
"outcome": null,
"start_time": "2026-09-15T12:00:00.000Z",
"end_time": "2026-09-15T12:45:00.000Z",
"duration_minutes": 45,
"location": "meet",
"meeting_url": "https://meet.google.com/abc-defg-hij",
"invitee": {
"name": "Camille Dubois",
"email": "camille.dubois@example.com",
"phone": "+33612345678",
"timezone": "Europe/Paris"
},
"host": {
"id": "4e8c5703-265c-45b2-9e76-9deeedb65007",
"name": "Malo Durand",
"email": "malo@example.com",
"timezone": "Europe/Paris"
},
"funnel": {
"id": "a1d4c8b2-6f39-4e07-9c15-3b8d2e6a4c71",
"slug": "appel-decouverte",
"name": "Appel découverte",
"url": "https://kalleo.io/synergie/appel-decouverte"
},
"calendar": {
"id": "e6b2f7a3-1c94-4d80-a5e2-7f3c9b1d0428",
"key": "hot",
"name": "Séniors uniquement"
},
"lead": {
"id": "9f1c2d64-4b1e-4a1f-9a5e-1f0b7f3c2a11",
"status": "booked",
"score": 4.2,
"tier": "hot",
"country": "FR",
"source_code": "AB58935",
"link": {
"code": "AB58935",
"name": "rentree-2026",
"url": "https://synergie.example/offre?kalleo=AB58935",
"landing_page": {
"name": "synergie.example/offre",
"url": "https://synergie.example/offre"
}
},
"source": {
"name": "rentree-2026",
"platform": "meta",
"format": "video-1"
},
"utm": {
"source": "meta",
"medium": "paid_social",
"campaign": "rentree-2026",
"content": "video-1",
"term": null
},
"answers": [
{
"question_id": "budget",
"question": "Quel est votre budget ?",
"value": "more-5k",
"label": "Plus de 5 000 €"
},
{
"question_id": "decision",
"question": "Êtes-vous décisionnaire ?",
"value": "yes",
"label": "Oui"
}
]
},
"notes": "Disponible surtout le matin.",
"manage_url": "https://kalleo.io/rdv/eyJhbGciOiJIUzI1NiJ9.example",
"created_at": "2026-09-12T09:31:04.000Z"
}401 — Missing, unknown or revoked API key.
404 — Not found in your organization.
Cancel a booking
Frees the slot, removes the Google Calendar event and emails both the invitee and the closer. Idempotent: cancelling twice sends nothing twice.
| Parameter | In | Description |
|---|---|---|
| id* | path | Booking identifier (UUID). |
Request body
{
"reason": "Prospect asked to postpone."
}Response 200
{
"id": "3a7d51e0-08c6-4a3b-9c1d-6f2b5d8e4a90",
"object": "booking",
"status": "cancelled",
"outcome": null,
"start_time": "2026-09-15T12:00:00.000Z",
"end_time": "2026-09-15T12:45:00.000Z",
"duration_minutes": 45,
"location": "meet",
"meeting_url": "https://meet.google.com/abc-defg-hij",
"invitee": {
"name": "Camille Dubois",
"email": "camille.dubois@example.com",
"phone": "+33612345678",
"timezone": "Europe/Paris"
},
"host": {
"id": "4e8c5703-265c-45b2-9e76-9deeedb65007",
"name": "Malo Durand",
"email": "malo@example.com",
"timezone": "Europe/Paris"
},
"funnel": {
"id": "a1d4c8b2-6f39-4e07-9c15-3b8d2e6a4c71",
"slug": "appel-decouverte",
"name": "Appel découverte",
"url": "https://kalleo.io/synergie/appel-decouverte"
},
"calendar": {
"id": "e6b2f7a3-1c94-4d80-a5e2-7f3c9b1d0428",
"key": "hot",
"name": "Séniors uniquement"
},
"lead": {
"id": "9f1c2d64-4b1e-4a1f-9a5e-1f0b7f3c2a11",
"status": "booked",
"score": 4.2,
"tier": "hot",
"country": "FR",
"source_code": "AB58935",
"link": {
"code": "AB58935",
"name": "rentree-2026",
"url": "https://synergie.example/offre?kalleo=AB58935",
"landing_page": {
"name": "synergie.example/offre",
"url": "https://synergie.example/offre"
}
},
"source": {
"name": "rentree-2026",
"platform": "meta",
"format": "video-1"
},
"utm": {
"source": "meta",
"medium": "paid_social",
"campaign": "rentree-2026",
"content": "video-1",
"term": null
},
"answers": [
{
"question_id": "budget",
"question": "Quel est votre budget ?",
"value": "more-5k",
"label": "Plus de 5 000 €"
},
{
"question_id": "decision",
"question": "Êtes-vous décisionnaire ?",
"value": "yes",
"label": "Oui"
}
]
},
"notes": "Disponible surtout le matin.",
"manage_url": "https://kalleo.io/rdv/eyJhbGciOiJIUzI1NiJ9.example",
"created_at": "2026-09-12T09:31:04.000Z",
"already_cancelled": false
}401 — Missing, unknown or revoked API key.
404 — Not found in your organization.
Record a meeting result
Marks the meeting as attended — with its outcome when known — or as a no-show. Only once the meeting has started, never on a cancelled one. Emits `booking.completed` or `booking.no_show`.
| Parameter | In | Description |
|---|---|---|
| id* | path | Booking identifier (UUID). |
Request body
{
"status": "completed",
"outcome": "won"
}Response 200
{
"id": "3a7d51e0-08c6-4a3b-9c1d-6f2b5d8e4a90",
"object": "booking",
"status": "completed",
"outcome": "won",
"start_time": "2026-09-15T12:00:00.000Z",
"end_time": "2026-09-15T12:45:00.000Z",
"duration_minutes": 45,
"location": "meet",
"meeting_url": "https://meet.google.com/abc-defg-hij",
"invitee": {
"name": "Camille Dubois",
"email": "camille.dubois@example.com",
"phone": "+33612345678",
"timezone": "Europe/Paris"
},
"host": {
"id": "4e8c5703-265c-45b2-9e76-9deeedb65007",
"name": "Malo Durand",
"email": "malo@example.com",
"timezone": "Europe/Paris"
},
"funnel": {
"id": "a1d4c8b2-6f39-4e07-9c15-3b8d2e6a4c71",
"slug": "appel-decouverte",
"name": "Appel découverte",
"url": "https://kalleo.io/synergie/appel-decouverte"
},
"calendar": {
"id": "e6b2f7a3-1c94-4d80-a5e2-7f3c9b1d0428",
"key": "hot",
"name": "Séniors uniquement"
},
"lead": {
"id": "9f1c2d64-4b1e-4a1f-9a5e-1f0b7f3c2a11",
"status": "booked",
"score": 4.2,
"tier": "hot",
"country": "FR",
"source_code": "AB58935",
"link": {
"code": "AB58935",
"name": "rentree-2026",
"url": "https://synergie.example/offre?kalleo=AB58935",
"landing_page": {
"name": "synergie.example/offre",
"url": "https://synergie.example/offre"
}
},
"source": {
"name": "rentree-2026",
"platform": "meta",
"format": "video-1"
},
"utm": {
"source": "meta",
"medium": "paid_social",
"campaign": "rentree-2026",
"content": "video-1",
"term": null
},
"answers": [
{
"question_id": "budget",
"question": "Quel est votre budget ?",
"value": "more-5k",
"label": "Plus de 5 000 €"
},
{
"question_id": "decision",
"question": "Êtes-vous décisionnaire ?",
"value": "yes",
"label": "Oui"
}
]
},
"notes": "Disponible surtout le matin.",
"manage_url": "https://kalleo.io/rdv/eyJhbGciOiJIUzI1NiJ9.example",
"created_at": "2026-09-12T09:31:04.000Z"
}400 — Invalid status or outcome.
401 — Missing, unknown or revoked API key.
404 — Not found in your organization.
409 — The meeting has not started yet, or is cancelled.
Leads
Prospects who started or completed the qualification form.
List leads
Prospects captured by your forms, most recent first. `partial` leads started the form without finishing it.
| Parameter | In | Description |
|---|---|---|
| status | query | Comma-separated list among `partial`, `form`, `booked`. |
| tier | query | Comma-separated list among `cold`, `warm`, `hot`. |
| funnel_id | query | Only leads of this funnel. |
| query | Exact email (case-insensitive). | |
| created_after | query | Leads created at or after this instant. |
| created_before | query | Leads created at or before this instant. |
| updated_after | query | Leads updated at or after this instant. |
| sort | query | `created_at:desc` (default), `created_at:asc`, `updated_at:desc`, `updated_at:asc`. |
| limit | query | Page size, 1–100. Defaults to 20. |
| offset | query | Number of items to skip. Use `pagination.next_offset` from the previous page. |
Response 200
{
"data": [
{
"id": "9f1c2d64-4b1e-4a1f-9a5e-1f0b7f3c2a11",
"object": "lead",
"status": "form",
"name": "Camille Dubois",
"email": "camille.dubois@example.com",
"phone": "+33612345678",
"country": "FR",
"score": 4.2,
"tier": "hot",
"funnel": {
"id": "a1d4c8b2-6f39-4e07-9c15-3b8d2e6a4c71",
"slug": "appel-decouverte",
"name": "Appel découverte",
"url": "https://kalleo.io/synergie/appel-decouverte"
},
"source_code": "AB58935",
"link": {
"code": "AB58935",
"name": "rentree-2026",
"url": "https://synergie.example/offre?kalleo=AB58935",
"landing_page": {
"name": "synergie.example/offre",
"url": "https://synergie.example/offre"
}
},
"source": {
"name": "rentree-2026",
"platform": "meta",
"format": "video-1"
},
"utm": {
"source": "meta",
"medium": "paid_social",
"campaign": "rentree-2026",
"content": "video-1",
"term": null
},
"answers": [
{
"question_id": "budget",
"question": "Quel est votre budget ?",
"value": "more-5k",
"label": "Plus de 5 000 €"
},
{
"question_id": "decision",
"question": "Êtes-vous décisionnaire ?",
"value": "yes",
"label": "Oui"
}
],
"consents": [
{
"question_id": "consent",
"accepted": true,
"text": "J'accepte d'être recontacté(e) au sujet de ma demande.",
"at": "2026-09-12T09:30:12.000Z"
}
],
"booking_id": null,
"created_at": "2026-09-12T09:28:40.000Z",
"updated_at": "2026-09-12T09:30:12.000Z"
}
],
"pagination": {
"limit": 20,
"offset": 0,
"count": 1,
"total": 1,
"has_more": false,
"next_offset": null
}
}400 — A filter is malformed. The message names it.
401 — Missing, unknown or revoked API key.
Get a lead
One prospect with score, tier, resolved form answers and consents.
| Parameter | In | Description |
|---|---|---|
| id* | path | Lead identifier (UUID). |
Response 200
{
"id": "9f1c2d64-4b1e-4a1f-9a5e-1f0b7f3c2a11",
"object": "lead",
"status": "form",
"name": "Camille Dubois",
"email": "camille.dubois@example.com",
"phone": "+33612345678",
"country": "FR",
"score": 4.2,
"tier": "hot",
"funnel": {
"id": "a1d4c8b2-6f39-4e07-9c15-3b8d2e6a4c71",
"slug": "appel-decouverte",
"name": "Appel découverte",
"url": "https://kalleo.io/synergie/appel-decouverte"
},
"source_code": "AB58935",
"link": {
"code": "AB58935",
"name": "rentree-2026",
"url": "https://synergie.example/offre?kalleo=AB58935",
"landing_page": {
"name": "synergie.example/offre",
"url": "https://synergie.example/offre"
}
},
"source": {
"name": "rentree-2026",
"platform": "meta",
"format": "video-1"
},
"utm": {
"source": "meta",
"medium": "paid_social",
"campaign": "rentree-2026",
"content": "video-1",
"term": null
},
"answers": [
{
"question_id": "budget",
"question": "Quel est votre budget ?",
"value": "more-5k",
"label": "Plus de 5 000 €"
},
{
"question_id": "decision",
"question": "Êtes-vous décisionnaire ?",
"value": "yes",
"label": "Oui"
}
],
"consents": [
{
"question_id": "consent",
"accepted": true,
"text": "J'accepte d'être recontacté(e) au sujet de ma demande.",
"at": "2026-09-12T09:30:12.000Z"
}
],
"booking_id": null,
"created_at": "2026-09-12T09:28:40.000Z",
"updated_at": "2026-09-12T09:30:12.000Z"
}401 — Missing, unknown or revoked API key.
404 — Not found in your organization.
Delete a lead
GDPR erasure. The lead and its personal data disappear; its meetings stay in the calendar and in statistics, anonymised. Cannot be undone.
| Parameter | In | Description |
|---|---|---|
| id* | path | Lead identifier (UUID). |
Response 200
{
"id": "9f1c2d64-4b1e-4a1f-9a5e-1f0b7f3c2a11",
"object": "lead",
"deleted": true
}401 — Missing, unknown or revoked API key.
404 — Not found in your organization.
Webhooks
Subscriptions to events, delivered as signed POST requests.
List webhook subscriptions
Every subscription of the organization, without secrets.
Response 200
{
"data": [
{
"id": "7c1b9e40-2a58-4d3f-b6c9-8e5a1d0f2b74",
"object": "webhook",
"url": "https://hooks.example.com/kalleo",
"events": [
"booking.created",
"booking.cancelled"
],
"funnel_ids": null,
"status": "active",
"source": "api",
"consecutive_failures": 0,
"last_delivery_at": "2026-09-12T09:31:06.000Z",
"last_error": null,
"created_at": "2026-09-10T15:00:00.000Z",
"updated_at": "2026-09-10T15:00:00.000Z"
}
]
}401 — Missing, unknown or revoked API key.
Subscribe to events
Registers a URL for one or more events, for all your funnels or only some. Idempotent on the URL: subscribing the same URL again replaces its events and its funnel filter, and reactivates it. The response is the only place the signing `secret` is shown.
Request body
{
"url": "https://hooks.example.com/kalleo",
"events": [
"booking.created",
"booking.cancelled"
]
}400 — Invalid URL, unknown event or unknown funnel.
401 — Missing, unknown or revoked API key.
Get a webhook subscription
One subscription, without its secret.
| Parameter | In | Description |
|---|---|---|
| id* | path | Webhook identifier (UUID). |
Response 200
{
"id": "7c1b9e40-2a58-4d3f-b6c9-8e5a1d0f2b74",
"object": "webhook",
"url": "https://hooks.example.com/kalleo",
"events": [
"booking.created",
"booking.cancelled"
],
"funnel_ids": null,
"status": "active",
"source": "api",
"consecutive_failures": 0,
"last_delivery_at": "2026-09-12T09:31:06.000Z",
"last_error": null,
"created_at": "2026-09-10T15:00:00.000Z",
"updated_at": "2026-09-10T15:00:00.000Z"
}401 — Missing, unknown or revoked API key.
404 — Not found in your organization.
Update a webhook subscription
Change the URL, the events, the funnel filter, or pause/resume deliveries. Resuming resets the failure counter. A field you leave out is not changed.
| Parameter | In | Description |
|---|---|---|
| id* | path | Webhook identifier (UUID). |
Request body
{
"status": "paused"
}400 — Invalid field.
401 — Missing, unknown or revoked API key.
404 — Not found in your organization.
Delete a webhook subscription
Idempotent: deleting a subscription that no longer exists returns `deleted: false`, not an error.
| Parameter | In | Description |
|---|---|---|
| id* | path | Webhook identifier (UUID). |
Response 200
{
"id": "7c1b9e40-2a58-4d3f-b6c9-8e5a1d0f2b74",
"object": "webhook",
"deleted": true
}401 — Missing, unknown or revoked API key.
Receiving events
Subscribe a URL with POST /webhooks and Kalleo will POST a JSON envelope for each event, as it happens. Exactly one of booking or lead is present, with the same shape as the corresponding GET endpoint. Pass funnel_ids to listen to some tunnels only.
| Event | Carries | When |
|---|---|---|
| booking.created | booking | A prospect booked a meeting. |
| booking.cancelled | booking | A meeting was cancelled, by the prospect or by your team. |
| booking.completed | booking | The closer marked the meeting as attended, with its outcome when known (won, follow_up, lost). |
| booking.no_show | booking | The closer marked the prospect as absent. |
| lead.completed | lead | A prospect finished the qualification form. Score and tier are known from this point. |
| lead.not_booked | lead | A prospect started or finished the form and has not booked after 10 minutes without activity. Sent once, and once more if a started form is completed later, still without a booking. |
Envelope. id is stable per event: use it to deduplicate.
{
"id": "evt_booking_created_3a7d51e0-08c6-4a3b-9c1d-6f2b5d8e4a90",
"type": "booking.created",
"created_at": "2026-09-12T09:31:05.000Z",
"organization": {
"id": "0b2c1d4e-5f60-4a7b-8c9d-0e1f2a3b4c5d",
"slug": "synergie",
"name": "Synergie"
},
"booking": {
"id": "3a7d51e0-08c6-4a3b-9c1d-6f2b5d8e4a90",
"object": "booking",
"status": "confirmed",
"outcome": null,
"start_time": "2026-09-15T12:00:00.000Z",
"end_time": "2026-09-15T12:45:00.000Z",
"duration_minutes": 45,
"location": "meet",
"meeting_url": "https://meet.google.com/abc-defg-hij",
"invitee": {
"name": "Camille Dubois",
"email": "camille.dubois@example.com",
"phone": "+33612345678",
"timezone": "Europe/Paris"
},
"host": {
"id": "4e8c5703-265c-45b2-9e76-9deeedb65007",
"name": "Malo Durand",
"email": "malo@example.com",
"timezone": "Europe/Paris"
},
"funnel": {
"id": "a1d4c8b2-6f39-4e07-9c15-3b8d2e6a4c71",
"slug": "appel-decouverte",
"name": "Appel découverte",
"url": "https://kalleo.io/synergie/appel-decouverte"
},
"calendar": {
"id": "e6b2f7a3-1c94-4d80-a5e2-7f3c9b1d0428",
"key": "hot",
"name": "Séniors uniquement"
},
"lead": {
"id": "9f1c2d64-4b1e-4a1f-9a5e-1f0b7f3c2a11",
"status": "booked",
"score": 4.2,
"tier": "hot",
"country": "FR",
"source_code": "AB58935",
"link": {
"code": "AB58935",
"name": "rentree-2026",
"url": "https://synergie.example/offre?kalleo=AB58935",
"landing_page": {
"name": "synergie.example/offre",
"url": "https://synergie.example/offre"
}
},
"source": {
"name": "rentree-2026",
"platform": "meta",
"format": "video-1"
},
"utm": {
"source": "meta",
"medium": "paid_social",
"campaign": "rentree-2026",
"content": "video-1",
"term": null
},
"answers": [
{
"question_id": "budget",
"question": "Quel est votre budget ?",
"value": "more-5k",
"label": "Plus de 5 000 €"
},
{
"question_id": "decision",
"question": "Êtes-vous décisionnaire ?",
"value": "yes",
"label": "Oui"
}
]
},
"notes": "Disponible surtout le matin.",
"manage_url": "https://kalleo.io/rdv/eyJhbGciOiJIUzI1NiJ9.example",
"created_at": "2026-09-12T09:31:04.000Z"
}
}Where a lead comes from. funnel is the tunnel, as Kalleo calls it on screen: the form the prospect filled in. source is what Kalleo shows in its Source column: the name of the campaign, with its platform and its format. link is the tracked link they came through: its name, the address that was shared, and its landing page when it has one. For a direct visit, source.name is Direct and link is null. utm carries the tags of the link, completed by those of the page URL; without a link, those of the page URL.
Leads who did not book. lead.not_booked is sent when a prospect started or finished the form and has not booked after 10 minutes without activity, provided they left an email or a phone number. It also carries form, how far they went. It is sent once, and once more if a started form is completed later, still without a booking. Only forms started after you subscribed are reported.
{
"id": "evt_lead_not_booked_9f1c2d64-4b1e-4a1f-9a5e-1f0b7f3c2a11_form",
"type": "lead.not_booked",
"created_at": "2026-09-12T09:40:13.000Z",
"organization": {
"id": "0b2c1d4e-5f60-4a7b-8c9d-0e1f2a3b4c5d",
"slug": "synergie",
"name": "Synergie"
},
"lead": {
"id": "9f1c2d64-4b1e-4a1f-9a5e-1f0b7f3c2a11",
"object": "lead",
"status": "form",
"…": "…"
},
"form": {
"answered": 6,
"total": 6
}
}Headers. X-Kalleo-Event (the type), X-Kalleo-Delivery (unique per attempt) and X-Kalleo-Signature, an HMAC-SHA256 of <timestamp>.<raw body> with the subscription secret, formatted t=1757667065,v1=…. Verify it against the raw body, before parsing:
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyKalleoSignature(secret, header, rawBody, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const timestamp = Number(parts.t);
if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false;
const expected = createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
const given = Buffer.from(parts.v1 ?? "", "hex");
return given.length === 32 && timingSafeEqual(given, Buffer.from(expected, "hex"));
}Delivery. Answer with any 2xx within 10 seconds. Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours, then dropped. A 410 Gone pauses the subscription immediately; resume it with PATCH /webhooks/:id.
Zapier & Make
Both apps use this API with an API key already issued to your organization, and register their own webhook subscriptions when you turn a Zap or a scenario on — tagged with their source.
- Zapier — triggers New Booking, Booking Cancelled, Booking Completed, Booking No-Show, New Qualified Lead, Lead Did Not Book, each for all your tunnels or one of them; searches Find Booking, Find Lead; actions Cancel Booking, Record Meeting Result, Delete Lead. Accept the invitation
- Make — instant triggers Watch Bookings and Watch Leads (qualified leads, and leads who did not book), for all your tunnels or some of them; searches List Bookings, List Leads, List Tunnels, List Members, actions Get / Cancel a Booking, Record Meeting Result, Get / Delete a Lead, and Make an API Call for everything else. Install the app