Booking API
All endpoints accept the API key. organization_id, space_id, and space_group_id are always required on create.
6.1 Create a Booking — POST /api/bookings
Use this on every new event in your calendar.
Field Reference
| Field | Type | Required | Notes |
|---|---|---|---|
| organization_id | UUID | yes | |
| space_id | UUID | yes | Must be in third_party mode with an approved connection. |
| space_group_id | UUID | yes | Must match the space’s group. |
| title | string | yes | Max 255 chars. |
| description | string | no | Free-form. |
| start_time | ISO-8601 | yes | UTC. |
| end_time | ISO-8601 | yes | Must be after start_time. |
| attendees | array | yes | Pass [] if none. Convention: { name, email }. |
| total_amount | number ≥ 0 | yes | Pass 0 if payment handled outside ZenSpace. |
| deposit_amount | number ≥ 0 | yes | Pass 0 for third_party. |
| organizer_email | optional* | Optional in third_party mode; required in zenspace/hybrid. | |
| booking_source | enum | no | Recommended: external. |
| metadata.external_event_id | string | strongly rec. | Your own event ID for dedupe. |
Key Behaviors for third_party Mode
-
Immediately confirmed and paid. No Stripe payment intent is created. Payment is handled by your system.
-
Organizer emails are opt-in per meeting space via the email_notification flag on the space (defaults to false).
-
Scheduler forwarding and booking-access credential generation proceed normally. Credentials (magic link / lock PIN / wifi voucher) are always generated at T-15 if a physical-space mapping exists.
-
No webhook is fired back to you for this creation (it came from you).
Precondition Errors
| Condition | HTTP | Message |
|---|---|---|
| No connection exists for the space | 400 | Cannot create booking: No third-party connection found... |
| No approved connection | 400 | Cannot create booking: No approved third-party connection found... |
| Any pending/suspended connection exists | 400 | Cannot create booking: Third-party connection(s) are not in approved state... |
| Time overlap with another non-cancelled booking | 400 | Time slot already booked |
| Voucher applied without organizer_email | 400 | organizer_email is required when applying a voucher |
6.2 Update a Booking — PUT /api/bookings/{id}
-
Only send fields you want to change.
-
Time changes are validated against overlaps and business hours the same way as on create.
-
Do not use update to cancel — use POST /:id/cancel.
6.3 Cancel a Booking — POST /api/bookings/{id}/cancel
-
Idempotent against already-cancelled bookings: returns 400 if cancelled again.
-
Scheduler forwarding is cleaned up automatically.
-
Organizer cancellation email is sent if organizer_email is set.
-
Refunds: third_party bookings have no ZenSpace payment. Process refunds in the originating system.
6.4 Get a Single Booking — GET /api/bookings/{id}
Returns the full booking object. Useful for reconciling state after a webhook or checking whether a booking still exists.
6.5 Bulk Create — POST /api/bookings/bulk
For importing an existing calendar during initial sync. Accepts an array of bookings and processes them sequentially.
Use sparingly. Failures per-row don’t stop the batch; inspect the failed array.
6.6 List Bookings — GET /api/bookings
This endpoint requires JWT auth (read:bookings permission), not an API key. Your bridge cannot call it directly. Reconciliation from the bridge side is done via stored (external_event_id, zenspace_booking_id) mappings and GET /api/bookings/{id}.