REST API for managing profiles, events, bookings, availability, calendar intelligence, and scheduling requests.
https://skdul.ai/api/v1Generate an API key from Dashboard → MCP Server → API Keys. Keys start with sk_live_.
# 1. Find available slots for an event type
curl "https://skdul.ai/api/v1/availability/slots?eventTypeId=<id>&startDate=2026-06-01&endDate=2026-06-07" \
-H "Authorization: Bearer sk_live_<your_key>"
# 2. Book one of the returned slots
curl -X POST https://skdul.ai/api/v1/bookings \
-H "Authorization: Bearer sk_live_<your_key>" \
-H "Content-Type: application/json" \
-d '{
"eventTypeId": "<id>",
"startTime": "2026-06-02T14:00:00Z",
"endTime": "2026-06-02T14:30:00Z",
"guestName": "John Smith",
"guestEmail": "john@example.com"
}'
# → 201 { "bookingId": "...", "eventTitle": "30 Minute Meeting" }
# → 409 booking_conflict if the slot was just taken — fetch fresh slots and retryAll /api/v1/* endpoints require an API key passed in the Authorization header as a Bearer token.
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxKeep keys secret. Do not commit them to version control, embed them in client-side code, or share them in public repositories. Rotate any key you believe has been exposed from Dashboard → MCP Server → API Keys.
HTTP/1.1 401 Unauthorized
{
"error": {
"type": "authentication_error",
"code": "api_key_missing",
"message": "No API key provided. Include your key as 'Authorization: Bearer sk_live_...'.",
"doc_url": "https://skdul.ai/docs/api#error-handling"
}
}All errors return a structured JSON object — never a flat string. The error.type field classifies the failure category; error.code gives the machine-readable cause to switch on in client code. When a specific parameter caused the error, it is named in error.param.
{
"error": {
"type": "invalid_request_error", // error category
"code": "missing_required_param", // machine-readable cause
"message": "Missing required param: 'guestEmail'.",
"param": "guestEmail", // present when a param is at fault
"doc_url": "https://skdul.ai/docs/api#error-handling"
}
}HTTP/1.1 404 Not Found
{
"error": {
"type": "not_found_error",
"code": "resource_not_found",
"message": "No such booking: '7c8d9e0f-1a2b-3456-cdef-012345678901'.",
"doc_url": "https://skdul.ai/docs/api#error-handling"
}
}HTTP/1.1 409 Conflict
{
"error": {
"type": "conflict_error",
"code": "slot_unavailable",
"message": "The requested time is outside the host's available hours. Use GET /api/v1/availability/slots to find open times.",
"doc_url": "https://skdul.ai/docs/api#error-handling"
}
}| Field | Always | Description |
|---|---|---|
error.type | Yes | Error category: authentication_error, authorization_error, invalid_request_error, not_found_error, conflict_error, api_error. |
error.code | Yes | Machine-readable cause. Switch on this in client code — see the table below for all codes. |
error.message | Yes | Human-readable explanation. Includes the offending resource ID on 404s and the resolution path on 409s. |
error.param | No | The request parameter that caused the error, when applicable. |
error.doc_url | Yes | Permalink to this errors reference. |
| Code | Meaning | error.code | When it occurs |
|---|---|---|---|
400 | Bad Request | missing_required_paraminvalid_param_valueinvalid_action | A required parameter is missing or a parameter value is invalid. The error.param field names the offending field. |
401 | Unauthorized | api_key_missingapi_key_invalid | Missing or invalid API key. Verify the Authorization header is present and the key is active in the dashboard. |
403 | Forbidden | access_denied | The API key is valid but the authenticated user does not own the requested resource. |
404 | Not Found | resource_not_found | The resource ID does not exist or belongs to another account. The message includes the exact ID that was not found. |
409 (booking_conflict) | Conflict | booking_conflict | Another booking was confirmed at this time slot between your request and the server. Fetch fresh slots and retry with a new time. |
409 (slot_unavailable) | Conflict | slot_unavailable | The requested time falls outside the host's configured working hours. Use GET /api/v1/availability/slots to find valid times. |
429 | Too Many Requests | rate_limit_error | Request rate exceeded. Back off and retry after the interval specified in the Retry-After header. |
500 | Internal Server Error | internal_error | An unexpected server error. Retry with exponential backoff. If the problem persists, contact support. |
Every error.code the API can return, with its type, HTTP status, an example message, and the condition that triggers it.
| error.code | type | HTTP | Example message | When it fires |
|---|---|---|---|---|
api_key_missing | authentication_error | 401 | No API key provided. Include your key as 'Authorization: Bearer sk_live_...'. | The Authorization header is absent or does not start with 'Bearer sk_live_'. |
api_key_invalid | authentication_error | 401 | Invalid API key. Check your key in the dashboard under MCP Server → API Keys. | The key is not found in the database, or has been revoked. |
access_denied | authorization_error | 403 | You do not have permission to access this resource. | The API key is valid but the authenticated user does not own the requested resource. |
missing_required_param | invalid_request_error | 400 | Missing required param: 'eventTypeId'. | A required body or query parameter is absent. The error.param field names the missing field. |
invalid_param_value | invalid_request_error | 400 | Slug must contain only lowercase letters, numbers, and hyphens (max 60 chars). | A parameter is present but fails validation (wrong format, out of range, invalid enum value). The error.param field names the offending field. |
invalid_action | invalid_request_error | 400 | 'action' must be 'accept' or 'reject'. | An action or enum field contains an unrecognised value. The error.param field names the field. |
resource_not_found | not_found_error | 404 | No such booking: '7c8d9e0f-1a2b-3456-cdef-012345678901'. | The resource ID does not exist or belongs to another account. The message always includes the exact ID that was not found. |
booking_conflict | conflict_error | 409 | This time slot is already booked. | Another booking was confirmed at the same slot between your request and the server. Fetch fresh slots via GET /api/v1/availability/slots and retry with a new time. |
slot_unavailable | conflict_error | 409 | The requested time is outside the host's available hours. Use GET /api/v1/availability/slots to find open times. | The requested time falls outside the host's configured working hours. No retry at the same time will succeed — fetch available slots first. |
internal_error | api_error | 500 | An unexpected error occurred. If this problem persists, contact support. | An unhandled server exception occurred. Retry with exponential backoff. If the problem persists, contact support with the request details. |
The API does not enforce hard rate limits. As a courtesy, keep requests under 60 per minute per API key. If you exceed this threshold you may receive a 429 response. The Retry-After header on 429 responses tells you how many seconds to wait. Use exponential backoff starting at 1 second for any retry logic.
List endpoints accept a limit query parameter (max 100, default 20) and return a flat JSON array — not wrapped in a data envelope. An empty result set returns []. If the number of results equals your limit, there may be more — narrow results further using filters like status or guestEmail. Cursor-based pagination with a has_more flag is on the roadmap.
The API is versioned via the URL path — all endpoints documented here are under /api/v1/. We follow a conservative breaking-change policy: additive changes (new optional fields, new optional parameters, new endpoints) are shipped without a version bump. Removals, type changes, or renamed parameters are breaking changes and will be announced with at least 30 days notice before being released under /api/v2/.
Booking creation is implicitly idempotent — attempting to book a slot that is already taken returns 409 Conflict rather than creating a duplicate. For other POST endpoints, safe retries are your responsibility. Explicit Idempotency-Key header support is on the roadmap.
Core objects returned across the API. Field types, nullable flags, and enum values shown here apply everywhere the object appears — you do not need to re-derive them from individual endpoint responses.
Represents a confirmed meeting between a host and a guest. Created via POST /api/v1/bookings or automatically when a scheduling request is accepted.
| Field | Type | Description |
|---|---|---|
id | string (uuid) | Unique booking identifier. |
event_type_id | string (uuid) | The event type this booking belongs to. |
host_id | string (uuid) | User ID of the host. |
start_time | string (ISO 8601) | Meeting start time in UTC. |
end_time | string (ISO 8601) | Meeting end time in UTC. |
duration_minutes | number | Duration of the meeting in minutes. |
guest_name | string | Guest's full name. |
guest_email | string | Guest's email address. |
guest_timezonenullable | string | Guest's IANA timezone at time of booking. |
guest_notesnullable | string | Optional notes submitted by the guest. |
status | string | Current booking status. One of: confirmed, cancelled_by_host, cancelled_by_guest, rescheduled, completed, no_show |
location_type | string | Meeting format. One of: video, phone, in_person, custom |
meeting_urlnullable | string | Video conference URL when location_type is video. |
source | string | How the booking was created. One of: mcp, api, booking_page, scheduling_request |
reschedule_count | number | Number of times this booking has been rescheduled. |
original_booking_idnullable | string (uuid) | ID of the booking this was rescheduled from, if applicable. |
created_at | string (ISO 8601) | When the booking was created. |
{
"id": "7c8d9e0f-1a2b-3456-cdef-012345678901",
"event_type_id": "3b4c5d6e-7f8a-9012-bcde-f01234567890",
"host_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"start_time": "2026-03-15T14:00:00Z",
"end_time": "2026-03-15T14:30:00Z",
"duration_minutes": 30,
"guest_name": "John Smith",
"guest_email": "john@example.com",
"guest_timezone": "America/Chicago",
"guest_notes": "Looking forward to it",
"status": "confirmed",
"location_type": "video",
"meeting_url": "https://zoom.us/j/123456789",
"source": "booking_page",
"reschedule_count": 0,
"original_booking_id": null,
"created_at": "2026-03-10T09:42:00Z"
}A meeting template that defines duration, location, buffer times, and booking constraints. Each event type has a public booking page at /{username}/{slug}.
| Field | Type | Description |
|---|---|---|
id | string (uuid) | Unique event type identifier. |
user_id | string (uuid) | Owner's user ID. |
title | string | Display name shown on the booking page. |
slug | string | URL-safe identifier. Booking page is at /{username}/{slug}. |
duration_minutes | number | Default meeting duration in minutes. |
descriptionnullable | string | Description shown to guests before booking. |
location_type | string | Meeting format. One of: video, phone, in_person, custom |
buffer_before | number | Minutes blocked before each meeting. |
buffer_after | number | Minutes blocked after each meeting. |
minimum_notice | number | Minimum advance notice required, in minutes. |
max_per_daynullable | number | Maximum bookings per day. null = unlimited. |
colornullable | string | Display color hex code. |
is_active | boolean | Whether this event type is open for new bookings. |
is_secret | boolean | If true, the event type is not listed on the public profile page. |
scheduling_type | string | Meeting format type. One of: one_on_one, group |
created_at | string (ISO 8601) | When the event type was created. |
{
"id": "3b4c5d6e-7f8a-9012-bcde-f01234567890",
"user_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "30 Minute Meeting",
"slug": "30min",
"duration_minutes": 30,
"description": "Quick chat to discuss your project",
"location_type": "video",
"buffer_before": 0,
"buffer_after": 5,
"minimum_notice": 60,
"max_per_day": null,
"color": "#E8634A",
"is_active": true,
"is_secret": false,
"scheduling_type": "one_on_one",
"created_at": "2026-01-15T10:00:00Z"
}The authenticated user's account and display settings. Controls how the booking page looks and how availability is calculated.
| Field | Type | Description |
|---|---|---|
id | string (uuid) | Unique user identifier. |
username | string | URL-safe username used in booking page URLs. |
full_namenullable | string | Display name shown on booking pages. |
email | string | Account email address. |
timezone | string | IANA timezone identifier used for availability calculations. |
time_format | string | Clock format. One of: 12h, 24h |
brand_colornullable | string | Hex color code used on the booking page. |
bionullable | string | Short bio visible on the public booking page. |
week_start | string | First day of the week on calendar views. One of: sunday, monday |
avatar_urlnullable | string | URL of the profile avatar image. |
plan | string | Current billing plan. One of: free, pro |
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"username": "janedoe",
"full_name": "Jane Doe",
"email": "jane@example.com",
"timezone": "America/New_York",
"time_format": "12h",
"brand_color": "#E8634A",
"bio": "Product designer based in NYC",
"week_start": "sunday",
"avatar_url": "https://skdul.ai/avatars/janedoe.jpg",
"plan": "pro"
}An async meeting negotiation between two skdul users. The engine finds mutual availability and proposes a slot — the recipient accepts or rejects it. Accepting automatically creates a confirmed Booking.
| Field | Type | Description |
|---|---|---|
id | string (uuid) | Scheduling request identifier. |
initiator_user_id | string (uuid) | User ID of the person who sent the request. |
target_username | string | Username of the request recipient. |
title | string | Meeting title proposed by the initiator. |
duration_minutes | number | Proposed meeting duration in minutes. |
status | string | Current request status. One of: pending, accepted, rejected, cancelled, expired |
matched_slot_startnullable | string (ISO 8601) | Start of the mutually available slot found by the engine. |
matched_slot_endnullable | string (ISO 8601) | End of the matched slot. |
notesnullable | string | Optional context message from the initiator. |
created_at | string (ISO 8601) | When the request was created. |
expires_atnullable | string (ISO 8601) | When the request auto-expires if not responded to. |
{
"id": "9e0f1a2b-3c4d-5678-defa-123456789012",
"initiator_user_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"target_username": "janedoe",
"title": "Product feedback session",
"duration_minutes": 30,
"status": "pending",
"matched_slot_start": "2026-03-15T15:00:00Z",
"matched_slot_end": "2026-03-15T15:30:00Z",
"notes": "Would love to get your thoughts on the new feature",
"created_at": "2026-03-10T10:00:00Z",
"expires_at": "2026-03-17T10:00:00Z"
}/api/v1/profileRetrieves the authenticated user's full profile. The response includes display settings, branding configuration, and the current plan. Use this to populate scheduling pages or sync profile data to your own systems.
Returns Returns a Profile object.
| Field | Type | Description |
|---|---|---|
id | string (uuid) | Unique user identifier. |
username | string | URL-safe username used in booking page URLs. |
full_namenullable | string | Display name shown on booking pages. |
email | string | Account email address. |
timezone | string | IANA timezone identifier (e.g. America/New_York). |
time_format | string | Clock format. One of: 12h, 24h |
brand_colornullable | string | Hex color code used on the booking page. |
bionullable | string | Short bio visible on the public booking page. |
week_start | string | First day of the week on calendar views. One of: sunday, monday |
avatar_urlnullable | string | URL of the profile avatar image. |
plan | string | Current billing plan. One of: free, pro |
curl https://skdul.ai/api/v1/profile \
-H "Authorization: Bearer sk_live_<your_key>"{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"username": "janedoe",
"full_name": "Jane Doe",
"email": "jane@example.com",
"timezone": "America/New_York",
"time_format": "12h",
"brand_color": "#E8634A",
"bio": "Product designer based in NYC",
"week_start": "sunday",
"avatar_url": "https://skdul.ai/avatars/janedoe.jpg",
"plan": "pro"
}/api/v1/profileUpdates one or more profile fields. Only include the fields you want to change — omitted fields are left untouched. Timezone changes affect how new availability slots are calculated but do not alter existing bookings.
Returns Returns the updated Profile object.
| Name | Type | Required | Description |
|---|---|---|---|
fullName | string | No | Display name shown on the booking page. |
timezone | string | No | IANA timezone (e.g. America/New_York). Affects future slot calculations. |
timeFormat | "12h" | "24h" | No | Time display format. |
brandColor | string | No | Hex color code (e.g. #E8634A). |
bio | string | No | Short bio for the public booking page (max 280 chars). |
weekStart | "sunday" | "monday" | No | First day of the week on calendar views. |
curl -X PATCH https://skdul.ai/api/v1/profile \
-H "Authorization: Bearer sk_live_<your_key>" \
-H "Content-Type: application/json" \
-d '{"fullName": "Jane Doe", "timezone": "America/New_York"}'{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"username": "janedoe",
"full_name": "Jane Doe",
"timezone": "America/New_York",
"time_format": "12h",
"brand_color": "#E8634A",
"bio": "Updated bio",
"week_start": "sunday"
}/api/v1/event-typesReturns all event types (meeting templates) for the authenticated user, sorted by creation date. Active events are included by default; pass includeInactive=true to also fetch disabled ones. Each event type defines the duration, location, buffer times, and booking constraints guests see.
Returns Returns an array of EventType objects.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
includeInactive | boolean | No | false | Include disabled event types in the response. |
| Field | Type | Description |
|---|---|---|
id | string (uuid) | Unique identifier for the event type. |
title | string | Display name shown on the booking page. |
slug | string | URL slug. Booking page is at /{username}/{slug}. |
duration_minutes | number | Meeting duration in minutes. |
descriptionnullable | string | Description shown to guests before booking. |
location_type | string | Meeting location type. One of: video, phone, in_person, custom |
buffer_before | number | Minutes blocked before each meeting. |
buffer_after | number | Minutes blocked after each meeting. |
minimum_notice | number | Minimum advance notice required in minutes. |
max_per_daynullable | number | Maximum bookings of this type per day. null = unlimited. |
colornullable | string | Display color hex code. |
is_active | boolean | Whether this event type is open for bookings. |
curl "https://skdul.ai/api/v1/event-types?includeInactive=true" \
-H "Authorization: Bearer sk_live_<your_key>"[
{
"id": "3b4c5d6e-7f8a-9012-bcde-f01234567890",
"title": "30 Minute Meeting",
"slug": "30min",
"duration_minutes": 30,
"description": "Quick chat to discuss your project",
"location_type": "video",
"buffer_before": 0,
"buffer_after": 5,
"minimum_notice": 60,
"max_per_day": null,
"color": "#E8634A",
"is_active": true
}
]/api/v1/event-typesCreates a new event type. The slug must be unique per user and URL-safe — it forms the path of the public booking page (/{username}/{slug}). Newly created event types are active by default. Buffer times and minimum notice are in minutes.
Returns Returns the created EventType object.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
title | string | Yes | — | Event title (e.g. '30 Minute Meeting'). |
slug | string | Yes | — | Unique URL slug (e.g. '30min'). Must be lowercase, no spaces. |
durationMinutes | number | Yes | — | Duration in minutes. |
description | string | No | — | Description shown on the booking page. |
locationType | "video" | "phone" | "in_person" | "custom" | No | video | Meeting location type. |
bufferBefore | number | No | 0 | Buffer minutes before the meeting. |
bufferAfter | number | No | 0 | Buffer minutes after the meeting. |
minimumNotice | number | No | 60 | Minimum advance notice in minutes before a booking is allowed. |
maxPerDay | number | No | — | Maximum bookings of this type per day. Omit for unlimited. |
color | string | No | — | Display color hex code. |
curl -X POST https://skdul.ai/api/v1/event-types \
-H "Authorization: Bearer sk_live_<your_key>" \
-H "Content-Type: application/json" \
-d '{
"title": "30 Minute Meeting",
"slug": "30min",
"durationMinutes": 30,
"locationType": "video",
"bufferAfter": 5
}'{
"id": "3b4c5d6e-7f8a-9012-bcde-f01234567890",
"title": "30 Minute Meeting",
"slug": "30min",
"duration_minutes": 30,
"location_type": "video",
"buffer_before": 0,
"buffer_after": 5,
"minimum_notice": 60,
"is_active": true
}/api/v1/event-types/[id]Updates any subset of fields on an existing event type. Use isActive to enable or disable bookings without deleting the event. Slug changes take effect immediately — share updated booking links with guests.
Returns Returns the updated EventType object.
| Name | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | The event type ID. |
| Name | Type | Required | Description |
|---|---|---|---|
title | string | No | Event title. |
slug | string | No | URL slug. |
durationMinutes | number | No | Duration in minutes. |
description | string | No | Description. |
locationType | "video" | "phone" | "in_person" | "custom" | No | Location type. |
bufferBefore | number | No | Buffer minutes before. |
bufferAfter | number | No | Buffer minutes after. |
minimumNotice | number | No | Minimum advance notice in minutes. |
maxPerDay | number | No | Maximum bookings per day. |
color | string | No | Display color hex code. |
isActive | boolean | No | Set to false to stop accepting new bookings. |
curl -X PATCH https://skdul.ai/api/v1/event-types/<id> \
-H "Authorization: Bearer sk_live_<your_key>" \
-H "Content-Type: application/json" \
-d '{"title": "Updated Meeting", "durationMinutes": 45}'{
"id": "3b4c5d6e-7f8a-9012-bcde-f01234567890",
"title": "Updated Meeting",
"slug": "30min",
"duration_minutes": 45,
"is_active": true
}/api/v1/event-types/[id]Permanently deletes an event type. Existing confirmed bookings for this event type are not cancelled — they remain active. If you want to stop new bookings without losing history, use PATCH with isActive: false instead.
Returns Returns a success confirmation.
| Name | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | The event type ID to delete. |
curl -X DELETE https://skdul.ai/api/v1/event-types/<id> \
-H "Authorization: Bearer sk_live_<your_key>"{ "success": true }/api/v1/availability/slotsReturns all available time slots for a given event type within a date range. Slots respect the host's working hours, buffer times, existing bookings, and synced external calendar events. The timezone parameter controls how slot times are expressed in the response — pass the guest's timezone for best results.
Returns Returns an array of TimeSlot objects.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
eventTypeId | string | Yes | — | The event type ID to check availability for. |
startDate | string | Yes | — | Start of the date range (YYYY-MM-DD). |
endDate | string | Yes | — | End of the date range (YYYY-MM-DD). Max 60-day range. |
timezone | string | No | UTC | IANA timezone for slot times (e.g. America/Chicago). |
| Field | Type | Description |
|---|---|---|
start | string (ISO 8601) | Slot start time in UTC. |
end | string (ISO 8601) | Slot end time in UTC. |
curl "https://skdul.ai/api/v1/availability/slots?eventTypeId=<id>&startDate=2026-03-10&endDate=2026-03-17&timezone=America/Chicago" \
-H "Authorization: Bearer sk_live_<your_key>"[
{ "start": "2026-03-10T14:00:00Z", "end": "2026-03-10T14:30:00Z" },
{ "start": "2026-03-10T14:30:00Z", "end": "2026-03-10T15:00:00Z" },
{ "start": "2026-03-10T15:00:00Z", "end": "2026-03-10T15:30:00Z" }
]/api/v1/bookingsReturns a paginated list of bookings for the authenticated user, sorted by start time descending. By default only upcoming confirmed bookings are returned. Use status and guestEmail to narrow results. The limit parameter caps the result set; cursor-based pagination is coming.
Returns Returns an array of Booking objects.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
status | string | No | — | Filter by status: confirmed, cancelled_by_host, cancelled_by_guest, rescheduled, completed, no_show. |
upcoming | boolean | No | true | When true, only returns bookings with a future start time. |
guestEmail | string | No | — | Filter bookings to a specific guest email address. |
limit | number | No | 20 | Maximum number of bookings to return (max 100). |
| Field | Type | Description |
|---|---|---|
id | string (uuid) | Booking identifier. |
event_type_id | string (uuid) | The event type this booking belongs to. |
start_time | string (ISO 8601) | Meeting start time in UTC. |
end_time | string (ISO 8601) | Meeting end time in UTC. |
guest_name | string | Guest's full name. |
guest_email | string | Guest's email address. |
guest_timezonenullable | string | Guest's IANA timezone at the time of booking. |
status | string | Current booking status. One of: confirmed, cancelled_by_host, cancelled_by_guest, rescheduled, completed, no_show |
guest_notesnullable | string | Notes the guest submitted when booking. |
meeting_urlnullable | string | Video conference URL (if location type is video). |
curl "https://skdul.ai/api/v1/bookings?status=confirmed&upcoming=true&limit=10" \
-H "Authorization: Bearer sk_live_<your_key>"[
{
"id": "7c8d9e0f-1a2b-3456-cdef-012345678901",
"event_type_id": "3b4c5d6e-7f8a-9012-bcde-f01234567890",
"start_time": "2026-03-15T14:00:00Z",
"end_time": "2026-03-15T14:30:00Z",
"guest_name": "John Smith",
"guest_email": "john@example.com",
"guest_timezone": "America/Chicago",
"status": "confirmed",
"guest_notes": "Looking forward to it",
"meeting_url": "https://zoom.us/j/123456789"
}
]/api/v1/bookingsCreates a confirmed booking for a guest. The slot must be within the event type's availability window — call GET /api/v1/availability/slots first to find valid times. Returns 409 if the slot is already taken. Confirmation emails are sent automatically to both the host and guest.
Returns Returns an object with the new booking ID and event title.
| Name | Type | Required | Description |
|---|---|---|---|
eventTypeId | string | Yes | The event type ID to book. |
startTime | string (ISO 8601) | Yes | Meeting start time in UTC. |
endTime | string (ISO 8601) | Yes | Meeting end time in UTC. |
guestName | string | Yes | Guest's full name. |
guestEmail | string | Yes | Guest's email address. Used for confirmation and reminder emails. |
durationMinutes | number | No | Duration in minutes. Defaults to the event type's configured duration if omitted. |
guestTimezone | string | No | Guest's IANA timezone. Used for email formatting. |
guestNotes | string | No | Optional notes from the guest. |
curl -X POST https://skdul.ai/api/v1/bookings \
-H "Authorization: Bearer sk_live_<your_key>" \
-H "Content-Type: application/json" \
-d '{
"eventTypeId": "<event_type_id>",
"startTime": "2026-03-15T14:00:00Z",
"endTime": "2026-03-15T14:30:00Z",
"guestName": "John Smith",
"guestEmail": "john@example.com",
"durationMinutes": 30
}'{
"bookingId": "7c8d9e0f-1a2b-3456-cdef-012345678901",
"eventTitle": "30 Minute Meeting"
}/api/v1/bookings/[id]Retrieves full details for a single booking, including the nested event type metadata. Use this to display booking confirmation pages or to sync booking details to your CRM.
Returns Returns a Booking object with a nested event_types field.
| Name | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | The booking ID. |
curl https://skdul.ai/api/v1/bookings/<id> \
-H "Authorization: Bearer sk_live_<your_key>"{
"id": "7c8d9e0f-1a2b-3456-cdef-012345678901",
"event_type_id": "3b4c5d6e-7f8a-9012-bcde-f01234567890",
"start_time": "2026-03-15T14:00:00Z",
"end_time": "2026-03-15T14:30:00Z",
"guest_name": "John Smith",
"guest_email": "john@example.com",
"guest_timezone": "America/Chicago",
"status": "confirmed",
"guest_notes": "Looking forward to it",
"meeting_url": "https://zoom.us/j/123456789",
"event_types": {
"title": "30 Minute Meeting",
"duration_minutes": 30,
"location_type": "video"
}
}/api/v1/bookings/[id]/cancelCancels a confirmed booking. Sets the status to cancelled_by_host, removes the event from the host's connected calendar, deletes the Zoom meeting if applicable, and sends a cancellation email to the guest. This action is irreversible — reschedule instead if a time change is needed.
Returns Returns a success confirmation. The booking status is updated to cancelled_by_host.
| Name | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | The booking ID to cancel. |
| Name | Type | Required | Description |
|---|---|---|---|
reason | string | No | Cancellation reason included in the guest notification email. |
curl -X POST https://skdul.ai/api/v1/bookings/<id>/cancel \
-H "Authorization: Bearer sk_live_<your_key>" \
-H "Content-Type: application/json" \
-d '{"reason": "Schedule conflict"}'{ "success": true }/api/v1/bookings/[id]/rescheduleReschedules an existing booking to a new time slot. Creates a new confirmed booking at the new time, marks the original as rescheduled, updates the calendar event and Zoom meeting, and sends rescheduling emails to both parties. The new slot must be available.
Returns Returns a success flag and the new booking's ID.
| Name | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | The booking ID to reschedule. |
| Name | Type | Required | Description |
|---|---|---|---|
newStartTime | string (ISO 8601) | Yes | New start time in UTC. |
newEndTime | string (ISO 8601) | Yes | New end time in UTC. |
curl -X POST https://skdul.ai/api/v1/bookings/<id>/reschedule \
-H "Authorization: Bearer sk_live_<your_key>" \
-H "Content-Type: application/json" \
-d '{
"newStartTime": "2026-03-16T10:00:00Z",
"newEndTime": "2026-03-16T10:30:00Z"
}'{
"success": true,
"newBookingId": "e5f6a7b8-c9d0-1234-efab-345678901234"
}/api/v1/scheduleReturns the host's weekly availability schedule — the days and hours during which bookings can be made. The schedule is used to compute available slots in conjunction with buffer times, minimum notice, and external calendar busy times.
Returns Returns a Schedule object with a rules array.
curl https://skdul.ai/api/v1/schedule \
-H "Authorization: Bearer sk_live_<your_key>"{
"schedule": {
"id": "5d6e7f8a-9b0c-1234-6789-abcdef012345",
"name": "Default",
"timezone": "America/New_York"
},
"rules": [
{ "day_of_week": 1, "start_time": "09:00", "end_time": "17:00", "is_enabled": true },
{ "day_of_week": 2, "start_time": "09:00", "end_time": "17:00", "is_enabled": true },
{ "day_of_week": 3, "start_time": "09:00", "end_time": "17:00", "is_enabled": true },
{ "day_of_week": 4, "start_time": "09:00", "end_time": "17:00", "is_enabled": true },
{ "day_of_week": 5, "start_time": "09:00", "end_time": "17:00", "is_enabled": true }
]
}/api/v1/scheduleReplaces the weekly availability schedule. Send the full rules array — this is a full replacement, not a merge. Days not included, or with isEnabled: false, will block all bookings on that day. Day numbering follows ISO 8601: 0 = Sunday, 6 = Saturday.
Returns Returns the updated Schedule object.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
rules | AvailabilityRule[] | Yes | — | Full set of availability rules. Replaces all existing rules. |
rules[].dayOfWeek | number (0–6) | Yes | — | Day of week. 0 = Sunday, 1 = Monday … 6 = Saturday. |
rules[].startTime | string | Yes | — | Start of availability window (HH:MM, 24h format). |
rules[].endTime | string | Yes | — | End of availability window (HH:MM, 24h format). |
rules[].isEnabled | boolean | No | true | Set to false to block this day without removing the rule. |
curl -X PUT https://skdul.ai/api/v1/schedule \
-H "Authorization: Bearer sk_live_<your_key>" \
-H "Content-Type: application/json" \
-d '{
"rules": [
{ "dayOfWeek": 1, "startTime": "09:00", "endTime": "17:00" },
{ "dayOfWeek": 3, "startTime": "09:00", "endTime": "17:00" },
{ "dayOfWeek": 5, "startTime": "09:00", "endTime": "17:00" }
]
}'{
"schedule": { "id": "5d6e7f8a-9b0c-1234-6789-abcdef012345", "name": "Default" },
"rules": [
{ "day_of_week": 1, "start_time": "09:00", "end_time": "17:00", "is_enabled": true },
{ "day_of_week": 3, "start_time": "09:00", "end_time": "17:00", "is_enabled": true },
{ "day_of_week": 5, "start_time": "09:00", "end_time": "17:00", "is_enabled": true }
]
}/api/v1/calendar/healthAnalyzes your calendar over a date range and returns a weighted health score (0–100) with detailed breakdown. The score factors in meeting density, back-to-back sequences, missing buffers, and available focus time. Use this to surface calendar burn-out signals in dashboards or AI assistants.
Returns Returns a CalendarHealthResult object.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
startDate | string | No | 14 days ago | Start of analysis range (YYYY-MM-DD). |
endDate | string | No | 14 days ahead | End of analysis range (YYYY-MM-DD). |
timezone | string | No | UTC | IANA timezone for date boundary calculations. |
curl "https://skdul.ai/api/v1/calendar/health?startDate=2026-03-01&endDate=2026-03-14&timezone=America/New_York" \
-H "Authorization: Bearer sk_live_<your_key>"{
"score": 72,
"period": { "startDate": "2026-03-01", "endDate": "2026-03-14" },
"totalMeetings": 24,
"totalHoursBooked": 12.5,
"avgMeetingsPerDay": 1.7,
"backToBackCount": 6,
"noBufferCount": 3,
"focusTimeHours": 18.2,
"longestFocusBlock": 180,
"overbookedDays": [
{ "date": "2026-03-05", "count": 7 }
],
"recommendations": [
"Add buffer time after meetings on Tuesdays",
"Consider blocking Thursday mornings for deep work"
]
}/api/v1/calendar/conflictsDetects double-booked or overlapping events in your calendar within a date range. Returns each conflicting pair with the overlapping time range. Useful for surfacing scheduling mistakes or auditing calendar state before important periods.
Returns Returns an object with a conflicts array.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
startDate | string | No | today | Start of detection range (YYYY-MM-DD). |
endDate | string | No | 7 days ahead | End of detection range (YYYY-MM-DD). |
curl "https://skdul.ai/api/v1/calendar/conflicts?startDate=2026-03-10&endDate=2026-03-17" \
-H "Authorization: Bearer sk_live_<your_key>"{
"conflicts": [
{
"eventA": { "id": "7c8d9e0f-1a2b-3456-cdef-012345678901", "title": "Team Standup", "start": "2026-03-10T14:00:00Z" },
"eventB": { "id": "8d9e0f1a-2b3c-4567-def0-123456789012", "title": "Client Call", "start": "2026-03-10T14:15:00Z" },
"overlapMinutes": 15
}
],
"count": 1
}/api/v1/calendar/focus-timeFinds uninterrupted blocks of free time in your calendar — ideal for deep work or batch scheduling. Blocks shorter than minBlockMinutes are excluded. Pass preferredStartHour and preferredEndHour to restrict results to a specific part of the day.
Returns Returns an array of FocusBlock objects.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
startDate | string | No | today | Start of search range (YYYY-MM-DD). |
endDate | string | No | 7 days ahead | End of search range (YYYY-MM-DD). |
minBlockMinutes | number | No | 90 | Minimum block duration in minutes. |
preferredStartHour | number (0–23) | No | — | Only return blocks starting at or after this hour. |
preferredEndHour | number (0–23) | No | — | Only return blocks ending at or before this hour. |
timezone | string | No | UTC | IANA timezone for hour filtering. |
curl "https://skdul.ai/api/v1/calendar/focus-time?minBlockMinutes=120&startDate=2026-03-10&endDate=2026-03-17" \
-H "Authorization: Bearer sk_live_<your_key>"{
"focusBlocks": [
{
"start": "2026-03-11T09:00:00Z",
"end": "2026-03-11T12:00:00Z",
"durationMinutes": 180,
"date": "2026-03-11"
},
{
"start": "2026-03-12T13:30:00Z",
"end": "2026-03-12T16:00:00Z",
"durationMinutes": 150,
"date": "2026-03-12"
}
],
"totalFocusHours": 5.5
}/api/v1/calendar/week-summaryReturns a structured summary of a single week's meetings, hours, and load distribution. Defaults to the current week. Use weekOf to inspect any past or future week. Results are segmented by day for charting.
Returns Returns a WeekSummary object.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
weekOf | string | No | current week | Any date within the target week (YYYY-MM-DD). |
timezone | string | No | UTC | IANA timezone for day boundary calculations. |
curl "https://skdul.ai/api/v1/calendar/week-summary?weekOf=2026-03-09&timezone=America/New_York" \
-H "Authorization: Bearer sk_live_<your_key>"{
"weekOf": "2026-03-09",
"totalMeetings": 11,
"totalHours": 5.5,
"byDay": [
{ "date": "2026-03-09", "dayName": "Monday", "meetings": 3, "hours": 1.5, "backToBack": 1 },
{ "date": "2026-03-10", "dayName": "Tuesday", "meetings": 2, "hours": 1.0, "backToBack": 0 },
{ "date": "2026-03-11", "dayName": "Wednesday", "meetings": 4, "hours": 2.0, "backToBack": 2 },
{ "date": "2026-03-12", "dayName": "Thursday", "meetings": 1, "hours": 0.5, "backToBack": 0 },
{ "date": "2026-03-13", "dayName": "Friday", "meetings": 1, "hours": 0.5, "backToBack": 0 }
]
}/api/v1/calendar/suggest-reschedulesAnalyses upcoming back-to-back meetings and overloaded days, then suggests specific bookings that could be moved and better times for them. Powered by the same scoring engine as smart booking. Useful for proactive calendar management in AI assistants.
Returns Returns an array of RescheduleRecommendation objects.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
timezone | string | No | UTC | IANA timezone for suggestion context. |
curl "https://skdul.ai/api/v1/calendar/suggest-reschedules?timezone=America/New_York" \
-H "Authorization: Bearer sk_live_<your_key>"{
"suggestions": [
{
"booking": {
"id": "7c8d9e0f-1a2b-3456-cdef-012345678901",
"title": "Weekly Sync",
"start": "2026-03-11T09:00:00Z"
},
"reason": "Back-to-back with prior meeting, no buffer",
"suggestedSlots": [
{ "start": "2026-03-11T11:00:00Z", "end": "2026-03-11T12:00:00Z" },
{ "start": "2026-03-12T14:00:00Z", "end": "2026-03-12T15:00:00Z" }
]
}
]
}/api/v1/calendar/syncTriggers a manual sync of the host's connected Google Calendar to refresh busy time data. By default syncs the last 7 days and next 30 days. Use forceFullSync for a complete historical resync (slower). The availability/slots endpoint uses this data automatically.
Returns Returns a sync result summary.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
daysBack | number | No | 7 | Days back from today to sync. |
daysAhead | number | No | 30 | Days ahead to sync. |
forceFullSync | boolean | No | false | If true, clears and resyncs all busy times (slower). |
curl -X POST https://skdul.ai/api/v1/calendar/sync \
-H "Authorization: Bearer sk_live_<your_key>" \
-H "Content-Type: application/json" \
-d '{"daysAhead": 60}'{
"synced": true,
"eventsProcessed": 42,
"daysBack": 7,
"daysAhead": 30
}/api/v1/preferencesLists the authenticated user's scheduling preference rules. Rules can be explicit (set via POST) or learned (inferred by the AI from booking patterns). Use includeLearnedRules=false to retrieve only manually set rules. Preferences power the smart booking slot scoring engine.
Returns Returns an array of Preference objects.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
ruleType | string | No | — | Filter by rule type: time_preference, day_preference, contact_rule, focus_block, max_meetings, buffer_rule, duration_rule, custom. |
contactEmail | string | No | — | Filter to preferences for a specific contact. |
includeLearnedRules | boolean | No | true | Whether to include AI-inferred rules alongside explicit ones. |
| Field | Type | Description |
|---|---|---|
id | string (uuid) | Preference rule identifier. |
rule_type | string | Category of the rule. One of: time_preference, day_preference, contact_rule, focus_block, max_meetings, buffer_rule, duration_rule, custom |
rule | object | Rule payload — structure varies by rule_type. |
contact_emailnullable | string | If set, rule applies only to this contact. |
contact_categorynullable | string | If set, rule applies to contacts in this category (e.g. VIP, teammate). |
priority | number | Higher values take precedence when rules conflict. |
descriptionnullable | string | Human-readable description of the rule. |
is_learned | boolean | True if inferred by AI, false if manually created. |
curl "https://skdul.ai/api/v1/preferences?includeLearnedRules=true" \
-H "Authorization: Bearer sk_live_<your_key>"[
{
"id": "2a3b4c5d-6e7f-8901-abcd-ef1234567890",
"rule_type": "time_preference",
"rule": { "preferMorning": true, "earliestHour": 9, "latestHour": 12 },
"contact_email": null,
"priority": 5,
"description": "Prefer morning slots",
"is_learned": false
},
{
"id": "3b4c5d6e-7f8a-9012-bcde-f12345678901",
"rule_type": "contact_rule",
"rule": { "preferDays": [1, 3] },
"contact_email": "vip@client.com",
"priority": 10,
"description": "Meet this client on Mon or Wed",
"is_learned": false
}
]/api/v1/preferencesCreates a new scheduling preference rule. Rules are applied by the slot scoring engine when computing best available times. Higher priority rules override lower ones when they conflict. Contact-scoped rules only apply when scheduling with that specific contact.
Returns Returns the created Preference object.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
ruleType | string | Yes | — | Rule category: time_preference, day_preference, contact_rule, focus_block, max_meetings, buffer_rule, duration_rule, custom. |
rule | object | Yes | — | Rule payload object. Structure depends on ruleType. |
contactEmail | string | No | — | Apply this rule only to a specific contact. |
contactCategory | string | No | — | Apply this rule to a category of contacts. |
eventTypeId | string | No | — | Scope this rule to a specific event type. |
priority | number | No | 5 | Rule priority (1–10). Higher values take precedence. |
description | string | No | — | Human-readable label for this rule. |
curl -X POST https://skdul.ai/api/v1/preferences \
-H "Authorization: Bearer sk_live_<your_key>" \
-H "Content-Type: application/json" \
-d '{
"ruleType": "time_preference",
"rule": { "preferMorning": true, "earliestHour": 9, "latestHour": 12 },
"priority": 5,
"description": "Prefer morning slots"
}'{
"id": "2a3b4c5d-6e7f-8901-abcd-ef1234567890",
"rule_type": "time_preference",
"rule": { "preferMorning": true, "earliestHour": 9, "latestHour": 12 },
"priority": 5,
"description": "Prefer morning slots",
"is_learned": false,
"created_at": "2026-03-01T10:00:00Z"
}/api/v1/preferences/[id]Deletes a preference rule. The deletion takes effect immediately — subsequent slot scoring calls will no longer apply this rule. Learned rules can be deleted to override AI-inferred preferences.
Returns Returns a success confirmation.
| Name | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | The preference ID to delete. |
curl -X DELETE https://skdul.ai/api/v1/preferences/<id> \
-H "Authorization: Bearer sk_live_<your_key>"{ "success": true }/api/v1/insightsReturns AI-computed scheduling insights derived from booking history. Insights cover patterns like busiest days, peak hours, back-to-back frequency, and weekly meeting load. Results are cached — call POST /api/v1/insights/recompute to refresh. Each insight includes a confidence score (0–1) based on data volume.
Returns Returns an array of Insight objects.
| Name | Type | Required | Description |
|---|---|---|---|
insightType | string | No | Filter to a specific insight type: busiest_day, peak_hours, back_to_back, weekly_load, duration_pattern. |
curl "https://skdul.ai/api/v1/insights?insightType=busiest_day" \
-H "Authorization: Bearer sk_live_<your_key>"[
{
"insightType": "busiest_day",
"summary": "Tuesday is your busiest day with 8 meetings on average",
"data": { "busiestDayName": "Tuesday", "busiestDayOfWeek": 2, "dayCounts": { "0": 1, "1": 5, "2": 8 } },
"confidence": 0.87
},
{
"insightType": "peak_hours",
"summary": "Most meetings are scheduled between 10am–12pm",
"data": { "peakStartHour": 10, "peakEndHour": 12, "hourCounts": { "9": 3, "10": 12, "11": 14, "12": 7 } },
"confidence": 0.91
}
]/api/v1/insights/recomputeTriggers a fresh recompute of all scheduling insights from the last 90 days of booking history. Recomputation is synchronous and typically takes under 500ms. Use this after importing a large batch of historical bookings or when insights feel stale.
Returns Returns an array of freshly computed Insight objects.
curl -X POST https://skdul.ai/api/v1/insights/recompute \
-H "Authorization: Bearer sk_live_<your_key>"[
{
"insightType": "busiest_day",
"summary": "Wednesday is your busiest day with 6 meetings on average",
"data": { "busiestDayName": "Wednesday", "busiestDayOfWeek": 3 },
"confidence": 0.82
}
]/api/v1/contacts/[email]/contextReturns the full scheduling history and learned context for a specific contact email. Includes past bookings, observed time preferences, and any contact-scoped preference rules. Use this before creating a booking request to surface the most convenient times for both parties.
Returns Returns a ContactContext object.
| Name | Type | Required | Description |
|---|---|---|---|
email | string (URL-encoded) | Yes | The contact's email address, URL-encoded. |
curl "https://skdul.ai/api/v1/contacts/john%40example.com/context" \
-H "Authorization: Bearer sk_live_<your_key>"{
"email": "john@example.com",
"totalMeetings": 8,
"lastMeetingAt": "2026-02-28T14:00:00Z",
"preferredDays": [1, 3],
"preferredHourRange": { "start": 10, "end": 14 },
"recentBookings": [
{
"id": "7c8d9e0f-1a2b-3456-cdef-012345678901",
"start_time": "2026-02-28T14:00:00Z",
"event_title": "30 Minute Meeting"
}
],
"rules": [
{
"id": "2a3b4c5d-6e7f-8901-abcd-ef1234567890",
"rule_type": "contact_rule",
"description": "Meet on Mon or Wed"
}
]
}/api/v1/scheduling-requestsLists scheduling requests where you are either the initiator or the recipient. Use direction to filter to sent or received requests. Pending requests have status=pending; accepted ones transition to accepted and generate a booking automatically.
Returns Returns an array of SchedulingRequest objects.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
direction | string | No | all | Which requests to return: sent, received, all. |
status | string | No | — | Filter by request status: pending, accepted, rejected, cancelled, expired. |
limit | number | No | 20 | Maximum number of results. |
| Field | Type | Description |
|---|---|---|
id | string (uuid) | Scheduling request identifier. |
initiator_user_id | string | User ID of the person who sent the request. |
target_username | string | Username of the request recipient. |
title | string | Meeting title proposed by the initiator. |
duration_minutes | number | Proposed meeting duration. |
status | string | Current status. One of: pending, accepted, rejected, cancelled, expired |
matched_slot_startnullable | string (ISO 8601) | The mutually available slot found by the engine. |
matched_slot_endnullable | string (ISO 8601) | End of the matched slot. |
notesnullable | string | Optional context message from the initiator. |
created_at | string (ISO 8601) | When the request was created. |
curl "https://skdul.ai/api/v1/scheduling-requests?direction=sent&status=pending" \
-H "Authorization: Bearer sk_live_<your_key>"[
{
"id": "9e0f1a2b-3c4d-5678-defa-123456789012",
"initiator_user_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"target_username": "janedoe",
"title": "Product feedback session",
"duration_minutes": 30,
"status": "pending",
"matched_slot_start": "2026-03-15T15:00:00Z",
"matched_slot_end": "2026-03-15T15:30:00Z",
"notes": "Would love to get your thoughts on the new feature",
"created_at": "2026-03-10T10:00:00Z"
}
]/api/v1/scheduling-requestsCreates a new scheduling request targeting another skdul user. The engine automatically finds mutual availability and proposes a matched slot — the recipient can accept or reject it. A notification email is sent to the target. Returns 400 if no mutual availability can be found.
Returns Returns the created SchedulingRequest object with a matched slot.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
targetUsername | string | Yes | — | The username of the person you want to schedule with. |
title | string | Yes | — | Meeting title shown to the recipient. |
durationMinutes | number | No | 30 | Desired meeting duration in minutes. |
locationType | string | No | video | Preferred meeting format: video, phone, in_person. |
notes | string | No | — | Optional context message to the recipient. |
preferences | object | No | — | Optional scheduling preferences (preferMorning, avoidDays, etc.) for slot matching. |
curl -X POST https://skdul.ai/api/v1/scheduling-requests \
-H "Authorization: Bearer sk_live_<your_key>" \
-H "Content-Type: application/json" \
-d '{
"targetUsername": "janedoe",
"title": "Product feedback session",
"durationMinutes": 30,
"notes": "Would love to get your thoughts on the new feature"
}'{
"requestId": "9e0f1a2b-3c4d-5678-defa-123456789012",
"status": "pending",
"matchedSlotStart": "2026-03-15T15:00:00Z",
"matchedSlotEnd": "2026-03-15T15:30:00Z"
}/api/v1/scheduling-requests/[id]Retrieves full details for a specific scheduling request including the matched slot and current status. Use this to poll request state or display booking confirmation UI.
Returns Returns a SchedulingRequest object.
| Name | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | The scheduling request ID. |
curl https://skdul.ai/api/v1/scheduling-requests/<id> \
-H "Authorization: Bearer sk_live_<your_key>"{
"id": "9e0f1a2b-3c4d-5678-defa-123456789012",
"target_username": "janedoe",
"title": "Product feedback session",
"duration_minutes": 30,
"status": "pending",
"matched_slot_start": "2026-03-15T15:00:00Z",
"matched_slot_end": "2026-03-15T15:30:00Z",
"notes": "Would love to get your thoughts",
"created_at": "2026-03-10T10:00:00Z"
}/api/v1/scheduling-requests/[id]/respondAccepts or rejects an incoming scheduling request. Accepting automatically creates a confirmed booking at the matched slot and notifies the initiator. Rejecting sends a rejection notification with an optional reason. Only the request recipient can respond.
Returns Accept: returns a Booking object. Reject: returns updated SchedulingRequest with status: rejected.
| Name | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | The scheduling request ID to respond to. |
| Name | Type | Required | Description |
|---|---|---|---|
action | "accept" | "reject" | Yes | Whether to accept or reject the request. |
rejectionReason | string | No | Optional explanation sent to the initiator when rejecting. |
curl -X POST https://skdul.ai/api/v1/scheduling-requests/<id>/respond \
-H "Authorization: Bearer sk_live_<your_key>" \
-H "Content-Type: application/json" \
-d '{"action": "accept"}'{
"bookingId": "7c8d9e0f-1a2b-3456-cdef-012345678901",
"status": "accepted",
"start_time": "2026-03-15T15:00:00Z",
"end_time": "2026-03-15T15:30:00Z"
}/api/v1/scheduling-requests/[id]/cancelCancels an outgoing scheduling request that has not yet been accepted. Can be called by the initiator. If the request was already accepted and a booking created, cancel the booking directly via POST /api/v1/bookings/{id}/cancel.
Returns Returns the cancelled SchedulingRequest with status: cancelled.
| Name | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | The scheduling request ID to cancel. |
curl -X POST https://skdul.ai/api/v1/scheduling-requests/<id>/cancel \
-H "Authorization: Bearer sk_live_<your_key>"{
"id": "9e0f1a2b-3c4d-5678-defa-123456789012",
"status": "cancelled"
}/api/v1/public/[username]/[slug]Returns a public event type for a given host username and event slug — no API key required. Use this to build embedded booking widgets or fetch event metadata for display. Also returns the host's public profile.
Returns Returns a public EventType with host profile embedded.
| Name | Type | Required | Description |
|---|---|---|---|
username | string | Yes | The host's skdul username. |
slug | string | Yes | The event type slug. |
curl https://skdul.ai/api/v1/public/janedoe/30min{
"event": {
"id": "3b4c5d6e-7f8a-9012-bcde-f01234567890",
"title": "30 Minute Meeting",
"slug": "30min",
"duration_minutes": 30,
"description": "Quick chat",
"location_type": "video"
},
"host": {
"username": "janedoe",
"full_name": "Jane Doe",
"bio": "Product designer",
"avatar_url": "https://skdul.ai/avatars/janedoe.jpg",
"brand_color": "#E8634A"
}
}/api/v1/public/[username]/event-typesLists all active public event types for a given host. No authentication required. Use this to populate a booking page with all available meeting types.
Returns Returns an array of public EventType objects.
| Name | Type | Required | Description |
|---|---|---|---|
username | string | Yes | The host's skdul username. |
curl https://skdul.ai/api/v1/public/janedoe/event-types[
{ "id": "3b4c5d6e-7f8a-9012-bcde-f01234567890", "title": "30 Minute Meeting", "slug": "30min", "duration_minutes": 30 },
{ "id": "4c5d6e7f-8a9b-0123-cdef-012345678901", "title": "60 Minute Strategy Call", "slug": "strategy", "duration_minutes": 60 }
]/api/v1/public/bookingsCreates a booking on behalf of a guest without an API key. Intended for embedding booking flows in third-party sites. Requires a hostId (the host's user ID, not API key) alongside the usual booking fields. Returns 409 if the slot is no longer available.
Returns Returns the created Booking object.
| Name | Type | Required | Description |
|---|---|---|---|
hostId | string (uuid) | Yes | The host's user ID. Obtain via GET /api/v1/public/[username]/[slug]. |
eventTypeId | string | Yes | Event type ID. |
startTime | string (ISO 8601) | Yes | Start time in UTC. |
endTime | string (ISO 8601) | Yes | End time in UTC. |
guestName | string | Yes | Guest's full name. |
guestEmail | string | Yes | Guest's email address. |
durationMinutes | number | Yes | Duration in minutes. |
guestTimezone | string | No | Guest's IANA timezone. |
guestNotes | string | No | Optional notes from the guest. |
curl -X POST https://skdul.ai/api/v1/public/bookings \
-H "Content-Type: application/json" \
-d '{
"hostId": "<host_user_id>",
"eventTypeId": "<event_type_id>",
"startTime": "2026-03-15T14:00:00Z",
"endTime": "2026-03-15T14:30:00Z",
"guestName": "John Smith",
"guestEmail": "john@example.com",
"durationMinutes": 30
}'{
"id": "7c8d9e0f-1a2b-3456-cdef-012345678901",
"event_type_id": "3b4c5d6e-7f8a-9012-bcde-f01234567890",
"start_time": "2026-03-15T14:00:00Z",
"end_time": "2026-03-15T14:30:00Z",
"guest_name": "John Smith",
"guest_email": "john@example.com",
"status": "confirmed"
}Set up your booking page in two minutes. No credit card required.
Start building with skdul