Schedule API and webhooks
What this page is — the external API for reading and writing Schedule data, and the events you can subscribe to.
What it is for — integrating: syncing appointments into another system, creating events from one, or reacting when a booking arrives.
The problem it solves — the in-product screens are for people. This is the interface for software.
Base path: /api/v1/ext/schedule · Authentication: an API key, scoped
1. Scopes
An API key is granted scopes. A call needs the scope its endpoint requires.
| Scope | Grants |
|---|---|
schedule:calendar:read | List the project's calendars |
schedule:config:read | Read event templates, bookable resources and availability |
schedule:event:read | Read events and their invitees |
schedule:event:write | Create and update events, and manage invitees — sends real invitations |
schedule:event:delete | Delete events |
schedule:booking:read | Read bookings made through a public booking page |
Calendars are created in the product, not through the API. schedule:calendar:write does not exist —
if you cannot find it, that is why.
2. Endpoints
| Method | Path | Scope |
|---|---|---|
| GET | /calendars | schedule:calendar:read |
| GET | /calendars/{id}/events | schedule:event:read |
| GET | /calendars/{id}/resources | schedule:config:read |
| GET | /calendars/{id}/availability | schedule:config:read |
| GET | /event-templates | schedule:config:read |
| GET | /bookings | schedule:booking:read |
| GET | /bookings/{id} | schedule:booking:read |
| POST | /events | schedule:event:write |
| GET | /events/{id} | schedule:event:read |
| PATCH | /events/{id} | schedule:event:write |
| DELETE | /events/{id} | schedule:event:delete |
| GET | /events/{id}/invitees | schedule:event:read |
| POST | /events/{id}/invitees | schedule:event:write |
| DELETE | /events/{id}/invitees/{inviteeId} | schedule:event:write |
POST /events and POST /events/{id}/invitees trigger the same notifications the product does. A test
run against a live calendar emails real people. Use a calendar with no connection configured while you
are developing — see event notifications.
POST requests are idempotent: repeating one with the same idempotency key will not create a second
event.
3. Event metadata is per calendar
An event's fields come from its calendar's template, so the payload for POST /events is not the same
for every calendar. Read GET /event-templates to discover what a given calendar expects before
writing to it, and send those fields as an object, not as a string containing JSON.
See calendar templates.
4. Webhooks
Eight events are published.
| Event | Fires when |
|---|---|
calendar.created | A calendar is created |
calendar.updated | A calendar's settings change |
calendar.status_changed | A calendar is activated or deactivated |
event.created | An event is created |
event.updated | An event is changed |
event.deleted | An event is deleted |
event.invitees_added | Participants are added to an event |
booking.created | A member of the public books through a booking page |
booking.created is the one most integrations want: it is the only event a person outside your
organisation can trigger, and it fires as the booking is written — see
what a booking creates.
5. Don't confuse this with…
| The public booking endpoints | The booking page's own endpoints are unauthenticated and rate-limited for visitors. They are not part of this API and are not for integrations |
| In-app permissions | API keys use scopes. The permissions in permissions do not apply to API calls |
6. Troubleshooting
| Symptom | Cause |
|---|---|
| 403 on a call | The key lacks that endpoint's scope |
| Cannot create a calendar | By design — §1 |
| Custom fields do not appear on the event | They were sent as a JSON string rather than an object — §3 |
| Real people received test invitations | §2. Develop against a calendar with no connection |