The calendar endpoints let an app read and change the calendars somebody has
access to. They are part of the [app API](/docs/api/apps/overview) and need a
user token, never an API key: a calendar belongs to people, and which people may
open it is a decision the environment already made.

Every one of them needs a `calendar:*` scope on the device and the
`view_calendar` permission on the account. Without the permission you get `403`
with `calendar_not_permitted`, whatever scopes the device holds.

## Ids

Every id is a string with a prefix that says what it names: `cal_` for a
calendar, `evt_` for an appointment and `ctag_` for a label, such as
`evt_k7m2p9x4q8r3t6v5`. Send them back exactly as you got them, in a URL, a
query and a body alike.

A number in their place never finds the row it used to name. In a URL it answers
`404`, and in `calendars[]` it is ignored like any calendar you cannot see. In a
JSON body `calendar_id` and `tag_ids[]` take a string, so a number there is
refused with `validation_failed`.

## The unit is an occurrence

A weekly meeting is **one row and many appearances**. The list returns
appearances, so a meeting every Monday for a year comes back as fifty two
objects that share an `id` and differ in `starts_at`.

That is why every write has to say two more things than you might expect:

| Field              | What it means                                                                                                                                                                                                                |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `occurrence_start` | Which appearance you mean, as the moment it originally started. Not the moment it starts now: an appearance somebody already moved is still named by where the rule put it. Leave it out on an appointment that happens once |
| `scope`            | How much of the series the write is about: `this`, `following` or `all`. Defaults to `this`, which is the one that cannot surprise anybody                                                                                   |

`this` on a repeating appointment creates a row of its own standing in for that
one appearance. `following` splits the series in two. `all` moves the whole
thing, keeping the changes anybody made to single appearances where they still
make sense.

## What you can see

Four levels, from an environment somebody already configured:

| Level    | What arrives                                                                                                                                  |
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `busy`   | That the hour is taken. `title` reads "Busy" and every other field is empty or null. Not a rendering choice: the words never leave the server |
| `read`   | Everything about the appointment                                                                                                              |
| `write`  | The same, and you may change it                                                                                                               |
| `manage` | The same, and it is yours to rename, share or delete                                                                                          |

The level is on every calendar in [the calendar list](/docs/api/calendar/calendars)
and `can_write` is on every appointment.

## Timezones

Everything on the wire is an instant in ISO 8601, in UTC. What an appointment
_means_ is carried by its `timezone`, which matters for repeating ones: a weekly
meeting at nine stays at nine when the clocks change, so its instants shift by an
hour twice a year and its `timezone` does not.

An appointment you create takes the timezone of the account that creates it, the
one its owner picked in their profile or the one their device last reported. An
account that has neither gives it the timezone of the calendar it goes into.

## Pages

- [List calendars](/docs/api/calendar/calendars)
- [List labels](/docs/api/calendar/tags)
- [List appointments](/docs/api/calendar/events)
- [Create an appointment](/docs/api/calendar/create)
- [Change an appointment](/docs/api/calendar/update)
- [Delete an appointment](/docs/api/calendar/delete)
- [Answer an invitation](/docs/api/calendar/respond)
- [Find a time](/docs/api/calendar/availability)