Developers

Kalleo API

Read your bookings and leads, cancel or qualify meetings, and receive every event by webhook — from your own code, from Zapier or from Make.

Base URL https://book.kombineo.com/api/v1OpenAPI 3.1Version 2026-10-16

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.

get/me

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.

get/funnels

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/funnels/{id}

Get a funnel

One booking page by identifier.

ParameterInDescription
id*pathFunnel 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.

get/members

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.

get/bookings

List bookings

Meetings of the organization, newest start first by default. Combine filters freely; dates are ISO 8601.

ParameterInDescription
statusqueryComma-separated list among `confirmed`, `cancelled`, `completed`, `no_show`.
funnel_idqueryOnly meetings booked on this funnel.
member_idqueryOnly meetings assigned to this closer.
calendar_idqueryOnly meetings on this calendar.
lead_idqueryOnly meetings of this lead.
invitee_emailqueryExact invitee email (case-insensitive).
min_start_timequeryMeetings starting at or after this instant.
max_start_timequeryMeetings starting at or before this instant.
created_afterqueryMeetings booked at or after this instant.
created_beforequeryMeetings booked at or before this instant.
sortquery`start_time:desc` (default), `start_time:asc`, `created_at:desc`, `created_at:asc`.
limitqueryPage size, 1–100. Defaults to 20.
offsetqueryNumber 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/bookings/{id}

Get a booking

One meeting with its invitee, host, funnel, calendar and lead (score, tier, form answers).

ParameterInDescription
id*pathBooking 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.

post/bookings/{id}/cancel

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.

ParameterInDescription
id*pathBooking 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.

post/bookings/{id}/result

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`.

ParameterInDescription
id*pathBooking 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.

get/leads

List leads

Prospects captured by your forms, most recent first. `partial` leads started the form without finishing it.

ParameterInDescription
statusqueryComma-separated list among `partial`, `form`, `booked`.
tierqueryComma-separated list among `cold`, `warm`, `hot`.
funnel_idqueryOnly leads of this funnel.
emailqueryExact email (case-insensitive).
created_afterqueryLeads created at or after this instant.
created_beforequeryLeads created at or before this instant.
updated_afterqueryLeads updated at or after this instant.
sortquery`created_at:desc` (default), `created_at:asc`, `updated_at:desc`, `updated_at:asc`.
limitqueryPage size, 1–100. Defaults to 20.
offsetqueryNumber 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/leads/{id}

Get a lead

One prospect with score, tier, resolved form answers and consents.

ParameterInDescription
id*pathLead 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/leads/{id}

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.

ParameterInDescription
id*pathLead 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.

get/webhooks

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.

post/webhooks

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/webhooks/{id}

Get a webhook subscription

One subscription, without its secret.

ParameterInDescription
id*pathWebhook 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.

patch/webhooks/{id}

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.

ParameterInDescription
id*pathWebhook identifier (UUID).

Request body

{
  "status": "paused"
}

400 — Invalid field.

401 — Missing, unknown or revoked API key.

404 — Not found in your organization.

delete/webhooks/{id}

Delete a webhook subscription

Idempotent: deleting a subscription that no longer exists returns `deleted: false`, not an error.

ParameterInDescription
id*pathWebhook 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.

EventCarriesWhen
booking.createdbookingA prospect booked a meeting.
booking.cancelledbookingA meeting was cancelled, by the prospect or by your team.
booking.completedbookingThe closer marked the meeting as attended, with its outcome when known (won, follow_up, lost).
booking.no_showbookingThe closer marked the prospect as absent.
lead.completedleadA prospect finished the qualification form. Score and tier are known from this point.
lead.not_bookedleadA 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