Bookings
3 endpoints
/v1/bookingsList bookings in the schedule
Returns the paginated list (cursor v2) of bookings in the schedule of
the key's account: all its calendars, all its hosts. Bookings
archived from the dashboard are excluded. Stable sort
start_datetime DESC, id DESC — the furthest-out bookings first,
then the history.
Filters
?from(ISO 8601) —start_datetime >= from?to(ISO 8601) —start_datetime <= to?status—confirmed,cancelled,completed, orno_show?event_type_id— a calendar of the account?contact_id— a contact of the account
An unreadable filter is rejected with 400 validation_error, with the
field and the reason (invalid_datetime, invalid_value,
invalid_format); an invalid cursor or limit, 400 invalid_request.
An event_type_id or a contact_id from another account returns an
empty list.
bookings:readQuery parameters
| Name | Type | Required | Description |
|---|---|---|---|
from | string | optional | Only bookings where start_datetime >= from. |
to | string | optional | Only bookings where start_datetime <= to. |
status | string | optional | Exact match on status. |
event_type_id | string | optional | Only bookings on this calendar. |
contact_id | string | optional | Only bookings for this contact. |
Responses
| Status | Description |
|---|---|
200 | Paginated list of bookings. |
400 | The request is malformed (missing header, invalid JSON, invalid parameter). |
401 | Unknown API key or invalid format. |
403 | Authorization denied. Two causes, distinguished by |
429 | The per-window request quota has been exceeded for this API key or endpoint. |
500 | Unexpected server error. Always include the |
Example request
curl -X GET 'https://api.capturia.io/v1/bookings' \
-H 'Authorization: Bearer cap_live_<your-key>' \
-H 'Content-Type: application/json'/v1/bookingsBook an appointment
Books an appointment in the account's schedule, on one of its calendars, through the same service as the booking page and the dashboard. The calendar's rules apply: its active hosts, their hours and their Google Calendar, the booking window, the slot cadence, the daily maximum, group sessions, required questions, the access rule, and a single upcoming booking per contact on this calendar. The requested time is honored or rejected with its reason — never replaced with another. The duration is the calendar's; the host receives their notification and the event is added to their calendar as for any booking.
Each successful call creates a booking: don't replay a call that succeeded.
Rejections
The details.reason field states why, in a stable way:
| Status | code | details.reason |
|---|---|---|
| 400 | validation_error | slot_in_past, outside_window, off_cadence (details.field = "start_datetime"), contact_or_guest_required, contact_without_email, or the code of the failing question (details.field = "form_responses.<id>", details.questions: the calendar's questions) |
| 404 | resource_not_found | — (details.field: event_type_id, contact_id, or host_id) |
| 409 | duplicate_resource | already_booked (details.existing_booking_id) |
| 422 | business_rule_violation | calendar_without_hosts, host_not_on_calendar, host_unavailable (details.causes), slot_unavailable, contact_archived, lead_not_qualified |
| 503 | service_unavailable | — (retry after Retry-After) |
A field format error (400 validation_error) carries details as a
list [{ field, reason }].
bookings:writeRequest body
| Property | Type | Required | Description |
|---|---|---|---|
event_type_id | string | required | Active calendar of the account. An unknown or inactive calendar, or
one from another account, is rejected with 404 resource_not_found
(details.field = "event_type_id"). |
start_datetime | string | required | Start of the booking, ISO 8601. The offset is optional: without an
offset, the time is read as UTC. Prefer an explicit offset
(2026-11-10T10:00:00-05:00). |
contact_id | string | optional | Existing contact of the account. An unknown contact or one from
another account is rejected with 404 resource_not_found
(details.field = "contact_id"); an archived contact, 422
(contact_archived); a contact without an email and without
guest_email, 400 (contact_without_email). |
guest_name | string | optional | Guest's full name. Required without contact_id. |
guest_email | string | optional | Guest's email. Required without contact_id. |
guest_phone | string | null | optional | Guest's phone number (E.164 recommended). Taken into account with guest_name + guest_email. |
host_id | string | optional | Team member who should hold the booking. The requested host is
honored or rejected with its reason, never replaced: unknown to the
account, 404 resource_not_found (details.field = "host_id"); not a
host of this calendar, 422 host_not_on_calendar; busy or outside
their hours, 422 host_unavailable. Without host_id: the contact's
sales rep if they can take the slot, otherwise the calendar's
rotation. |
timezone | string | null | optional | Guest's IANA time zone (e.g. America/Montreal). If absent: the
contact's, otherwise the calendar's. An unknown time zone or a bare
offset (-04:00) is rejected: 400 validation_error, reason
invalid_timezone. |
notes | string | null | optional | Free-form notes. |
form_responses | object | optional | Answers to the calendar's form: one entry per question, under the
question's identifier, in the form { "value": ... }. value is
text, a number for a numeric question, one of the options for a
choice question, or a list of options for a multiple-choice
question. A calendar with a required question rejects a booking
without its answer: 400 validation_error,
details.field = "form_responses.<id>". Every form rejection
includes details.questions, the calendar's questions (id, label,
type, required, options). |
Responses
| Status | Description |
|---|---|
201 | Booking created. |
400 | Parameter or body validation failed. |
401 | Unknown API key or invalid format. |
403 | Authorization denied. Two causes, distinguished by |
404 | The requested resource doesn't exist (or isn't within the tenant's scope). |
409 | The lead already has an upcoming booking on this calendar
( |
415 | The |
422 | The requested time or host can't be honored: calendar without an
active host ( |
429 | The per-window request quota has been exceeded for this API key or endpoint. |
500 | Unexpected server error. Always include the |
503 | Service temporarily unavailable (maintenance, external dependency down, degraded infrastructure). Clients should retry with exponential backoff. |
Example request
curl -X POST 'https://api.capturia.io/v1/bookings' \
-H 'Authorization: Bearer cap_live_<your-key>' \
-H 'Content-Type: application/json' \
-d '{
"event_type_id": "2b3c4d5e-6f7a-4b9c-8d1e-2f3a4b5c6d7e",
"start_datetime": "2026-11-10T10:00:00-05:00",
"contact_id": "0a1b2c3d-4e5f-4a7b-8c9d-0e1f2a3b4c5d",
"notes": "Premier rendez-vous découverte"
}'/v1/bookings/{id}Cancel a booking
Soft cancel — the booking is not deleted: its status changes
to cancelled and cancelled_at is timestamped, like a
cancellation made in the dashboard. The event is removed from the
host's calendar, the host is notified, and the cancellation is
recorded as made by one of your team's integrations. The 200
response returns these 3 fields (not 204 No Content).
Only a confirmed booking can be cancelled. A booking that is
already cancelled returns 422 business_rule_violation (reason
already_cancelled); a booking that was held or marked as a no-show,
422 (reason booking_not_cancellable). A booking from another
account is not found (404).
To change the time, cancel and book again, or reschedule from the
dashboard — there is no PATCH.
bookings:writeResponses
| Status | Description |
|---|---|
200 | Booking cancelled. Returns the cancellation status and timestamp. |
401 | Unknown API key or invalid format. |
403 | Authorization denied. Two causes, distinguished by |
404 | The requested resource doesn't exist (or isn't within the tenant's scope). |
422 | The booking is already cancelled ( |
429 | The per-window request quota has been exceeded for this API key or endpoint. |
500 | Unexpected server error. Always include the |
Example request
curl -X DELETE 'https://api.capturia.io/v1/bookings/{id}' \
-H 'Authorization: Bearer cap_live_<your-key>' \
-H 'Content-Type: application/json'