Booking links
Links people use to book a meeting type.
List your open single-use Scheduler links
/v1/booking-linksbooking-links.listNeeds the scheduler feature on your workspace. Until it is on, calls answer 404.
DEV-3936. The single-use links the caller made in this workspace that can still book: not used, not expired, and their meeting type not deleted. A team type's link belongs to the member who made it, so admins also see only their own. Newest first. Each item is { id, kind, meetingTypeId, personId, createdAt, expiresAt, availability } (availability null when the link uses its meeting type's times). There is no url: only a hash of the token is stored, so a link's URL is shown once, by POST /v1/booking-links. limit 1-200 (default 50), cursor from pagination.nextCursor. Needs the scheduler feature and the Calendar page (view).
Parameters
400 workspace_required).Response 200
data is a list of BookingLinkSummaryModel. Each item has these fields. The response also carries pagination.
curl -X GET "https://api.ken.so/v1/booking-links" \ -H "Authorization: Bearer $KEN_API_KEY" \ -H "Ken-Version: 2026-11-02" \ -H "X-Ken-Workspace-Id: $KEN_WORKSPACE_ID"
{
"success": true,
"data": [
{
"id": 812,
"kind": "string",
"meetingTypeId": 812,
"personId": 812,
"createdAt": "2026-09-23T15:02:11Z",
"expiresAt": "2026-09-23T15:02:11Z",
"availability": {
"timeZone": "string",
"weekly": {
"monday": [
{}
],
"tuesday": [
{}
],
"wednesday": [
{}
],
"thursday": [
{}
],
"friday": [
{}
],
"saturday": [
{}
],
"sunday": [
{}
]
},
"overrides": [
{
"date": "string",
"ranges": []
}
]
}
}
],
"pagination": {
"nextCursor": "eyJpZCI6ODE1fQ",
"hasMore": true,
"total": 1,
"isTotalCapped": true
}
}Make a booking link
/v1/booking-linksbooking-links.createNeeds the scheduler feature on your workspace. Until it is on, calls answer 404.
Body { kind, personId?, meetingTypeId?, hostMemberId?, accountId?, campaignId?, stepOrder?, touchId?, assetId?, source?, channel?, senderAddress?, availability? }. kind: person needs personId and returns the person's one link for that campaign (or Studio page, or source and host): 201 when made, 200 when it already existed. Its URL opens the page prefilled (?k=). kind: single_use returns a new /s/ link that books once and expires after 30 days; meetingTypeId defaults to the host's default type. Email and LinkedIn links never use a Kenso domain; senderAddress sends a campaign email's link to cal.<sending domain>. availability (single-use only; 400 on a person link) is the Scheduler link's own schedule, in the shape and rules of the member default on /v1/me/calendar-preferences: its slots use it instead of the meeting type's and the host's, with busy time, notice, window, buffers and the daily cap still on top. Returns { id, url, kind, expiresAt, availability } (availability null when none was sent; id is what PATCH /v1/booking-links/{bookingLinkId} takes). 409 invalid_state when no Scheduler domain can serve the link yet or the meeting type is off. Requires Calendar view; another member's page or type also needs the members feature.
Parameters
400 workspace_required).Request body
application/json, optional.
Response 201
Also answers 200. data is a BookingLinkModel.
curl -X POST "https://api.ken.so/v1/booking-links" \
-H "Authorization: Bearer $KEN_API_KEY" \
-H "Ken-Version: 2026-11-02" \
-H "X-Ken-Workspace-Id: $KEN_WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"kind": "string",
"personId": 812,
"meetingTypeId": 812,
"hostMemberId": 812,
"accountId": 812,
"campaignId": 812,
"stepOrder": 1,
"touchId": 812,
"assetId": 812,
"source": "string",
"channel": "string",
"senderAddress": "string",
"availability": {
"timeZone": "string",
"weekly": {
"monday": [
{
"start": "string",
"end": "string"
}
],
"tuesday": [
{
"start": "string",
"end": "string"
}
],
"wednesday": [
{
"start": "string",
"end": "string"
}
],
"thursday": [
{
"start": "string",
"end": "string"
}
],
"friday": [
{
"start": "string",
"end": "string"
}
],
"saturday": [
{
"start": "string",
"end": "string"
}
],
"sunday": [
{
"start": "string",
"end": "string"
}
]
},
"overrides": [
{
"date": "string",
"ranges": [
{
"start": "string",
"end": "string"
}
]
}
]
}
}'{
"success": true,
"data": {
"id": 812,
"url": "https://example.com",
"kind": "string",
"expiresAt": "2026-09-23T15:02:11Z",
"availability": {
"timeZone": "string",
"weekly": {
"monday": [
{
"start": "string",
"end": "string"
}
],
"tuesday": [
{
"start": "string",
"end": "string"
}
],
"wednesday": [
{
"start": "string",
"end": "string"
}
],
"thursday": [
{
"start": "string",
"end": "string"
}
],
"friday": [
{
"start": "string",
"end": "string"
}
],
"saturday": [
{
"start": "string",
"end": "string"
}
],
"sunday": [
{
"start": "string",
"end": "string"
}
]
},
"overrides": [
{
"date": "string",
"ranges": [
{}
]
}
]
}
}
}Change a single-use Scheduler link's times
/v1/booking-links/{bookingLinkId}booking-links.updateNeeds the scheduler feature on your workspace. Until it is on, calls answer 404.
DEV-3936. Body { availability }: the link's own schedule in the shape and rules of the create body (it may have no weekly times, such as only date overrides with ranges), or null to go back to the meeting type's times. Omitted: unchanged. Only the member who made the link changes it: another member's link, a person link and a link in another workspace are 404. A used or expired link is 409 invalid_state (link_used, link_expired). Returns the link as GET /v1/booking-links lists it. Needs the scheduler feature and the Calendar page (view).
Parameters
400 workspace_required).Request body
application/json, optional.
Response 200
data is a BookingLinkSummaryModel.
curl -X PATCH "https://api.ken.so/v1/booking-links/812" \
-H "Authorization: Bearer $KEN_API_KEY" \
-H "Ken-Version: 2026-11-02" \
-H "X-Ken-Workspace-Id: $KEN_WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"availability": {
"timeZone": "string",
"weekly": {
"monday": [
{
"start": "string",
"end": "string"
}
],
"tuesday": [
{
"start": "string",
"end": "string"
}
],
"wednesday": [
{
"start": "string",
"end": "string"
}
],
"thursday": [
{
"start": "string",
"end": "string"
}
],
"friday": [
{
"start": "string",
"end": "string"
}
],
"saturday": [
{
"start": "string",
"end": "string"
}
],
"sunday": [
{
"start": "string",
"end": "string"
}
]
},
"overrides": [
{
"date": "string",
"ranges": [
{
"start": "string",
"end": "string"
}
]
}
]
}
}'{
"success": true,
"data": {
"id": 812,
"kind": "string",
"meetingTypeId": 812,
"personId": 812,
"createdAt": "2026-09-23T15:02:11Z",
"expiresAt": "2026-09-23T15:02:11Z",
"availability": {
"timeZone": "string",
"weekly": {
"monday": [
{
"start": "string",
"end": "string"
}
],
"tuesday": [
{
"start": "string",
"end": "string"
}
],
"wednesday": [
{
"start": "string",
"end": "string"
}
],
"thursday": [
{
"start": "string",
"end": "string"
}
],
"friday": [
{
"start": "string",
"end": "string"
}
],
"saturday": [
{
"start": "string",
"end": "string"
}
],
"sunday": [
{
"start": "string",
"end": "string"
}
]
},
"overrides": [
{
"date": "string",
"ranges": [
{}
]
}
]
}
}
}Up to three open times on the person's own booking link, each a link that opens with the time picked
/v1/booking-links/suggested-timesbooking-links.suggested-timesNeeds the scheduler feature on your workspace. Until it is on, calls answer 404.
Query personId (required), meetingTypeId?, hostMemberId?, campaignId?, senderAddress?. The page is hostMemberId's (default: the caller; another member's needs the members feature) and the type one of that member's own (default: their default type). The times are the first open slot of each of the next days that have one (at most three, 14 days, host's zone), for the member the page routes the person to. Each time's url opens the page with that time picked; opening never books. A member on Calendly, Cal.com or HubSpot gets status: external_tool and that link, with no times. campaignId is the campaign of the thread being answered (404 when it is not this workspace's): a booking through the link counts for it. senderAddress is the mailbox the reply goes out from (400 when it is not an email address): with a campaign, the link lands on cal.<sending domain> when that host is ready. A client seat creates nothing: it gets times only on a link it already has for this person, else 409 invalid_state.
Parameters
400 workspace_required).Response 200
data is a SuggestedTimesModel.
curl -X GET "https://api.ken.so/v1/booking-links/suggested-times" \ -H "Authorization: Bearer $KEN_API_KEY" \ -H "Ken-Version: 2026-11-02" \ -H "X-Ken-Workspace-Id: $KEN_WORKSPACE_ID"
{
"success": true,
"data": {
"status": "string",
"reason": "string",
"bookingTool": "string",
"hostMemberId": 812,
"meetingTypeId": 812,
"routingReason": "string",
"timeZone": "string",
"url": "https://example.com",
"times": [
{
"startAt": "2026-09-23T15:02:11Z",
"endAt": "2026-09-23T15:02:11Z",
"localStartAt": "string",
"url": "https://example.com"
}
]
}
}