This guide walks you through the end-to-end flow for programmatically booking an event via the Zeeg API.
The availability and booking endpoints require a paid Zeeg subscription. Requests on free plans will be rejected.
Always verify availability before creating an event. Attempting to book a slot that is no longer available will result in an error. Check available time slots immediately before creating the event to avoid race conditions.
End-to-end booking flow
Get your scheduling page details
Retrieve the scheduling page (event type) you want to book against. You need its slug, the owner’s slug, any custom invitee questions, and whether a phone number is required.
List all your scheduling pages:
Or fetch a specific one by UUID:
Example response (key fields highlighted):
{
"resource": {
"uri": "https://api.zeeg.me/v2/event-types/80f46bf5-eb01-4c07-960e-a9a3e18aae5e",
"uuid": "80f46bf5-eb01-4c07-960e-a9a3e18aae5e",
"title": "30-Minute Discovery Call",
"type": "ONE_ON_ONE",
"slug": "30min-discovery-call",
"schedulingUrl": "https://zeeg.me/lena-meier/30min-discovery-call",
"isActive": true,
"duration": 30,
"profile": {
"type": "User",
"firstName": "Lena",
"lastName": "Meier",
"slug": "lena-meier",
"email": "lena.meier@horizondigital.de"
},
"inviteePhoneNumber": false,
"inviteeQuestions": [
{
"id": 1,
"order": 1,
"question": "What would you like to discuss?",
"isActive": true,
"isRequired": true,
"type": "TEXT_INPUT",
"options": null
}
]
}
}
slug — identifies the event type in subsequent requests (e.g., 30min-discovery-call).
profile.slug — the owner’s slug, used as ownerSlug (e.g., lena-meier).
inviteeQuestions — custom questions with their numeric ids. You will pass answers in step 3 using question_id.
inviteePhoneNumber — true if a phone number is required, false otherwise.
Check available time slots
Query the availability endpoint to find open slots for the scheduling page.
curl -X GET "https://api.zeeg.me/v2/availability/lena-meier/event-types/30min-discovery-call?startDate=2026-04-01&endDate=2026-04-14&timeZone=Europe/Berlin" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/json"
const response = await fetch(
"https://api.zeeg.me/v2/availability/lena-meier/event-types/30min-discovery-call?" +
new URLSearchParams({
startDate: "2026-04-01",
endDate: "2026-04-14",
timeZone: "Europe/Berlin",
}),
{
headers: {
Authorization: "Bearer YOUR_TOKEN",
Accept: "application/json",
},
}
);
const data = await response.json();
import requests
response = requests.get(
"https://api.zeeg.me/v2/availability/lena-meier/event-types/30min-discovery-call",
params={
"startDate": "2026-04-01",
"endDate": "2026-04-14",
"timeZone": "Europe/Berlin",
},
headers={
"Authorization": "Bearer YOUR_TOKEN",
"Accept": "application/json",
},
)
data = response.json()
{
"ownerSlug": "lena-meier",
"eventTypeSlug": "30min-discovery-call",
"schedulingUrl": "https://zeeg.me/lena-meier/30min-discovery-call",
"duration": 30,
"firstAvailableDate": "2026-04-01",
"lastAvailableDate": "2026-04-14",
"timeZone": "Europe/Berlin",
"timeZoneOffset": "+02:00",
"isNotAvailable": false,
"availability": [
{
"date": "2026-04-01",
"slots": ["09:00", "09:30", "10:00", "10:30", "14:00", "14:30", "15:00"]
},
{
"date": "2026-04-02",
"slots": ["09:00", "09:30", "10:00", "11:00", "14:00", "15:00"]
},
{
"date": "2026-04-03",
"slots": []
}
],
"currentTime": "2026-03-20T12:00:00+00:00"
}
This endpoint requires the admin:full or timetable scope.
Shared event types (Round Robin, Flexi, Collective):
Use shared as the ownerSlug instead of a specific user’s slug.
For Round Robin events, add withHostsCount=1 to the query to see how many hosts are available per slot.
Additional query parameters:
Add duration=45 to override the default meeting duration (useful when the scheduling page supports multiple durations).
Add seats=3 to check availability for group event types that support multiple attendees.
Shared links:
If you have a shared scheduling link, you can use the alternative endpoint:
GET /availability-by-shared-link/{sharedLink}?startDate=2026-04-01&endDate=2026-04-14&timeZone=Europe/Berlin
Book a slot by sending a POST request to /event with the slot details and invitee information.
curl -X POST "https://api.zeeg.me/v2/event" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"ownerSlug": "lena-meier",
"eventTypeSlug": "30min-discovery-call",
"date": "2026-04-15",
"start": "09:00",
"duration": 30,
"name": "Sophie Laurent",
"email": "sophie.laurent@northwind.io",
"timeZone": "Europe/Paris",
"guests": ["alex.chen@northwind.io"],
"location": "GOOGLE_MEET",
"question_answers": [
{ "question_id": 1, "answer": "Product demo and pricing options" }
]
}'
const response = await fetch("https://api.zeeg.me/v2/event", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_TOKEN",
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
ownerSlug: "lena-meier",
eventTypeSlug: "30min-discovery-call",
date: "2026-04-15",
start: "09:00",
duration: 30,
name: "Sophie Laurent",
email: "sophie.laurent@northwind.io",
timeZone: "Europe/Paris",
guests: ["alex.chen@northwind.io"],
location: "GOOGLE_MEET",
question_answers: [
{ question_id: 1, answer: "Product demo and pricing options" },
],
}),
});
const event = await response.json();
import requests
response = requests.post(
"https://api.zeeg.me/v2/event",
headers={
"Authorization": "Bearer YOUR_TOKEN",
"Content-Type": "application/json",
"Accept": "application/json",
},
json={
"ownerSlug": "lena-meier",
"eventTypeSlug": "30min-discovery-call",
"date": "2026-04-15",
"start": "09:00",
"duration": 30,
"name": "Sophie Laurent",
"email": "sophie.laurent@northwind.io",
"timeZone": "Europe/Paris",
"guests": ["alex.chen@northwind.io"],
"location": "GOOGLE_MEET",
"question_answers": [
{"question_id": 1, "answer": "Product demo and pricing options"},
],
},
)
event = response.json()
This endpoint requires the admin:full or booking scope.
| Field | Description |
|---|
ownerSlug | The scheduling page owner’s slug (from profile.slug in step 1) |
eventTypeSlug | The scheduling page slug (from step 1) |
date | The date of the slot (from step 2), e.g. 2026-04-01 |
start | The start time of the slot (from step 2), e.g. 09:00 |
duration | Duration in minutes (must match the event type duration) |
name | Invitee’s full name |
email | Invitee’s email address |
timeZone | Invitee’s time zone, e.g. Europe/Berlin |
| Field | Description |
|---|
phone | Invitee’s phone number (required if inviteePhoneNumber is true on the scheduling page) |
question_answers | Array of { question_id, answer } objects matching the inviteeQuestions from step 1 |
guests | Array of additional guest email addresses |
location | Location type: GOOGLE_MEET, ZOOM, MICROSOFT_TEAMS, IN_PERSON, PHONE_CALL |
locationOptions | Additional location details (see below) |
utm | UTM tracking object with keys: utm_campaign, utm_source, utm_medium, utm_content, utm_term |
customQueryParams | Custom URL query parameters to attach to the event. Keys must start with c__ (max 10 fields) |
sharedLink | Required for shared event types (Round Robin, Flexi, Collective) |
For IN_PERSON locations, include locationOptions with the meeting address:
{
"location": "IN_PERSON",
"locationOptions": {
"address": "123 Main St, Berlin, Germany",
"details": "3rd floor, room 301"
}
}
For PHONE_CALL locations, include locationOptions with the call type and a phone number:
{
"location": "PHONE_CALL",
"locationOptions": {
"callType": "I_WILL_CALL"
},
"phone": "+49 170 1234567"
}
The response from step 3 contains the created event with all its details.
{
"uri": "https://api.zeeg.me/v2/scheduled-events/zg-O69bac566950c6",
"uuid": "zg-O69bac566950c6",
"title": "30-Minute Discovery Call",
"type": "ONE_ON_ONE",
"startTime": "2026-04-15T07:00:00.000000Z",
"endTime": "2026-04-15T07:30:00.000000Z",
"duration": 30,
"status": "confirmed",
"eventTypeUri": "https://api.zeeg.me/v2/event-types/80f46bf5-eb01-4c07-960e-a9a3e18aae5e",
"location": {
"type": "Google Meet",
"joinUrl": "https://meet.google.com/abc-defg-hij"
},
"invitees": [
{
"uuid": "zg-O69bad4047abf0",
"fullName": "Sophie Laurent",
"email": "sophie.laurent@northwind.io",
"timeZone": "Europe/Paris",
"guests": ["alex.chen@northwind.io"]
}
],
"hosts": [
{
"firstName": "Lena",
"lastName": "Meier",
"email": "lena.meier@horizondigital.de",
"slug": "lena-meier"
}
]
}
To verify the booking at any later point, fetch it by UUID:
curl -X GET "https://api.zeeg.me/v2/scheduled-events/zg-O69bac566950c6" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/json"
const response = await fetch(
"https://api.zeeg.me/v2/scheduled-events/zg-O69bac566950c6",
{
headers: {
Authorization: "Bearer YOUR_TOKEN",
Accept: "application/json",
},
}
);
const event = await response.json();
import requests
response = requests.get(
"https://api.zeeg.me/v2/scheduled-events/zg-O69bac566950c6",
headers={
"Authorization": "Bearer YOUR_TOKEN",
"Accept": "application/json",
},
)
event = response.json()