Skip to main content
This page takes you from nothing to a real booking on your own account, in five requests. Every call runs against https://api.cal.id and is authenticated with a bearer token. Work through it in order — each step produces a value the next one needs, and the last step will fail if you skip ahead and invent a time rather than using one the API gave you.
You need a Cal ID account with at least one event type. If you have none, create one in the dashboard first — this guide reads an existing event type rather than creating one.
1

Get an API key

In the Cal ID dashboard, go to Settings → Developer → API Keys and create a key. Keys are prefixed with calid_ and are shown once, so copy it somewhere safe immediately.See Get your API key for the full walkthrough, including expiry options.
Your API key grants full access to your account. Never commit it to source control or put it in client-side code. Keep it in an environment variable:
2

Confirm the key works

Call GET /users/me. It is the cheapest way to prove your key and header are correct before you debug anything else.
A working key returns your profile inside data:
If you get 401 with error.code: "UNAUTHORIZED", the header is wrong or the key is invalid or expired. If you get a 404 whose body has no success field, you have mistyped the URL — that response comes from the router, not the API. See Error codes.
GET /users/me returns data as an object. Its sibling GET /users/{userslug} returns data as an array — read that one as data[0].
3

Find an event type ID

Every booking is made against an event type. List yours and pick one.
GET /event-types/ returns 10 records by default. If you omit limit, an account with 40 event types silently looks like it has 10, and the one you were after may simply not be in the response. Always pass limit explicitly. The maximum is 100; ?limit=1000 is rejected with 400 — querystring/limit must be <= 100.
The list arrives in data, with paging state in meta.pagination. Note the id of the event type you want to book, and its length in minutes — you need both.
You can also read the ID straight from the dashboard URL while editing an event — see Find your Event Type ID.
4

Fetch real bookable slots

Ask the API which times are actually available. Do not compute this yourself: availability is the product of schedules, buffers, minimum notice, booking limits, connected-calendar busy times, and timezone rules, and a reimplementation will not match.
On GET /slots/, eventTypeId is a string, not an integer. POST /booking/ in the next step takes the same ID as an integer. If you build requests from a typed client or a schema, you must convert between the two — sending the wrong type is rejected with 400.
The response nests slots two levels deep: data.slots is an object keyed by date ("YYYY-MM-DD"), and each value is an array of slot objects whose bookable time is in time.
Read your chosen time as data.slots["2026-09-08"][0].time — not data[0] and not data.slots[0].If the date key you expected is missing, or its array is empty, there is genuinely nothing bookable in that window. Widen start/end or check the event type’s schedule before moving on.
5

Create the booking

Post the slot you just read. start, end, and responses (with at least name and email) are required, and end is start plus the event type’s length in minutes.
Content-Type: application/json is mandatory. Omit it and the body is never parsed — you get 400 — body must be object even though the JSON is perfectly valid.
The created booking comes back in data, including its uid.

If the booking fails with a 500

The most common failure in this flow is:
Despite the 500, this is not a Cal ID outage — it means the start you sent is not a time the scheduling rules will accept. Almost always the cause is that start did not come from Step 4.
Do not retry a no_available_users_found_error with backoff. It is deterministic: every retry with the same start fails identically. Go back to GET /slots/ and use a time it actually returned.
Two things that look fine but are not:
  • A time that is obviously free on your calendar. Availability is not the same as “not busy” — buffers, minimum notice, and booking limits all remove otherwise-empty times.
  • A time echoed back from a booking object. Booking responses use startTime and endTime; the request body uses start and end. You cannot round-trip a booking object straight back into POST /booking/.

What you just learned

Go deeper with the API

API integration

The conventions that apply across every endpoint — envelopes, pagination, and the traps.

Error codes

Every error.code the API returns, and what to do about each.

Reserve a slot before booking

Hold a time while the booker completes checkout, so nobody else takes it.

Reschedule and cancel

Move or cancel a booking you created.

Webhooks

Get booking events pushed to you instead of polling.

API Reference

Every endpoint, with schemas and a live “Try it” playground.