{"openapi":"3.0.3","info":{"title":"Calendar API","version":"1.0.0","contact":{"name":"Source code","url":"https://github.com/Alexander-D-Karpov/calendar"},"description":"JSON API for calendars, events, todos and share links. All operations live under `/api/v1`.\n\n## Authentication\n\nSend an API token in the `Authorization` header:\n\n    Authorization: Bearer cal_\u003cprefix\u003e_\u003csecret\u003e\n\nCreate tokens in [Settings → API tokens](/settings/tokens). A token is shown once, right after it is created, and can be revoked at any time. To try requests on this page with a token, press **Authorize** and paste it.\n\nRequests from a signed-in browser session work too. With a session, unsafe methods (`POST`, `PUT`, `PATCH` and `DELETE`) also need the `X-CSRF-Token` header. **Try it out** on this page adds that header when you are signed in.\n\n## Scopes\n\nEach token carries one or more scopes, and each operation names the scope it needs. A `write` scope includes `read` access to the same resource. Browser sessions have every scope.\n\n| Scope | Allows |\n|---|---|\n| `account:read` | Reading the account profile |\n| `account:write` | Changing account settings and the sleep schedule |\n| `calendars:read` | Reading calendars and events |\n| `calendars:write` | Creating, changing and deleting calendars and events |\n| `todos:read` | Reading todo lists, todos and checks |\n| `todos:write` | Creating, changing, completing and deleting todos |\n| `shares:read` | Reading share links |\n| `shares:write` | Creating, changing, regenerating and revoking share links |\n| `imports:write` | Uploading and committing imports |\n| `exports:read` | Downloading ICS, CSV and YAML exports |\n| `sync:write` | Reading Google sync status and starting a sync |\n\nA token can already be created with the scopes marked above and will keep working\nonce the matching endpoints land, but nothing accepts them today.\n\n## MCP\n\nAgents use the same tokens over the Model Context Protocol at `/api/mcp` (Streamable HTTP, JSON responses, bearer token only). `tools/list` returns only the tools the token's scopes allow, and tool inputs match `EventInput` and `TodoInput` below.\n\n## Concurrency\n\nSingle resources carry an `ETag` header. Send it back in `If-Match` on `PATCH` and `DELETE` to fail with `412` instead of overwriting someone else's change. `PATCH` bodies are JSON Merge Patch: send only the fields to change, and `null` to clear a nullable field.\n\n## Idempotency\n\n`POST /events`, `/todos`, `/calendars`, `/todo-lists`, `/imports` and `/shares` accept an `Idempotency-Key` header of 1 to 255 characters. The first request under a key is carried out and its reply stored; repeating the key returns that same reply with `Idempotency-Replayed: true` instead of creating a second entry, which makes a retry after a dropped connection safe.\n\nReusing a key with a different body answers `422`. Repeating it while the first request is still running answers `409`. A key is only remembered for a successful request, so after a `4xx` you can correct the body and retry with the same one.\n\n## Errors\n\nErrors use `application/problem+json` as defined in RFC 9457. `detail` explains the problem, `errors` lists invalid fields on `422` responses, and `request_id` matches the `X-Request-ID` response header.\n\n## Rate limits\n\nAuthenticated responses carry `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`. Past the limit the API answers `429` with a `Retry-After` header. Repeated invalid tokens from one address are blocked the same way.\n"},"servers":[{"url":"/api/v1","description":"This server"}],"tags":[{"name":"Account","description":"The account that owns the token or session."},{"name":"Calendars","description":"Calendars group events. Every account has one default calendar that cannot be deleted."},{"name":"Events","description":"Events of calendars, including recurring series and occurrences changed on their own."},{"name":"Todo lists","description":"Lists group todos. Every account has one default list that cannot be deleted."},{"name":"Todos","description":"Todos with an optional due date and time, one level of subtasks, and recurrence on completion."},{"name":"Checks","description":"Checklist items inside a todo. Checking every item does not complete the todo."},{"name":"Imports","description":"Uploading a file, reviewing what it holds, and writing it in."},{"name":"Exports","description":"Downloading calendars and the whole account."},{"name":"Subscriptions","description":"ICS feeds followed as read-only calendars."},{"name":"Duplicates","description":"Entries that look like copies of each other, and how to resolve them."},{"name":"Trash","description":"Deleted events and todos, kept until the retention window passes."},{"name":"Search","description":"Finding events and todos by text and filters."},{"name":"Shares","description":"Read-only links to one fixed period of the calendar."},{"name":"Changes","description":"An ordered log of what changed, for catching up without refetching."},{"name":"Sync","description":"The Google Calendar and Tasks connection."}],"paths":{"/me":{"get":{"tags":["Account"],"operationId":"getMe","summary":"Get the current account","description":"Returns the account that owns the API token or browser session, with its display settings and sleep schedule. Use it to check that a token works.\n\nRequires scope `account:read`.\n","x-scope":"account:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"The current account.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"},"example":{"id":"01a08b9b-3dce-7ae6-b07c-a83315ccc2fe","email":"sasha@akarpov.ru","display_name":"Sasha","timezone":"Europe/Moscow","email_verified":true,"week_start":1,"time_format":"24h","default_view":"week","date_only_reminder":"09:00","sleep":{"enabled":true,"windows":[{"weekday":1,"start":"23:30","end":"07:30"}]},"created_at":"2026-09-10T13:56:42Z"}}}},"304":{"$ref":"#/components/responses/NotModified"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"patch":{"tags":["Account"],"operationId":"updateMe","summary":"Update account settings","description":"Changes the fields present in the body as a JSON Merge Patch. `sleep` replaces the whole schedule, so send every window to keep. Email and password cannot be changed here.\n\nRequires scope `account:write`.\n","x-scope":"account:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"}],"requestBody":{"required":true,"content":{"application/merge-patch+json":{"schema":{"$ref":"#/components/schemas/AccountInput"},"example":{"timezone":"Europe/Vilnius","sleep":{"enabled":true,"windows":[{"weekday":0,"start":"23:00","end":"07:00"}]}}},"application/json":{"schema":{"$ref":"#/components/schemas/AccountInput"},"example":{"time_format":"12h"}}}},"responses":{"200":{"description":"The updated account.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"},"example":{"id":"01a08b9b-3dce-7ae6-b07c-a83315ccc2fe","email":"sasha@akarpov.ru","display_name":"Sasha","timezone":"Europe/Moscow","email_verified":true,"week_start":1,"time_format":"24h","default_view":"week","date_only_reminder":"09:00","sleep":{"enabled":true,"windows":[{"weekday":1,"start":"23:30","end":"07:30"}]},"created_at":"2026-09-10T13:56:42Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/calendars":{"get":{"tags":["Calendars"],"operationId":"listCalendars","summary":"List calendars","description":"Returns every calendar of the account ordered by `position`. Hidden calendars are included.\n\nRequires scope `calendars:read`.\n","x-scope":"calendars:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"200":{"description":"All calendars of the account.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarList"},"example":{"items":[{"id":"0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","name":"Work","color":"#3b6ea5","description":"","timezone":"Europe/Moscow","kind":"local","read_only":false,"is_default":false,"hidden":false,"position":1,"default_reminders":[10],"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T14:02:11Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"post":{"tags":["Calendars"],"operationId":"createCalendar","summary":"Create a calendar","description":"Creates a local calendar. Only `name` is required. The calendar is placed after the others unless `position` is set. An account can have at most 100 calendars.\n\nRequires scope `calendars:write`.\n","x-scope":"calendars:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarInput"},"example":{"name":"Work","color":"#3b6ea5","timezone":"Europe/Moscow","default_reminders":[10]}}}},"responses":{"201":{"description":"The created calendar.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Location":{"$ref":"#/components/headers/Location"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Calendar"},"example":{"id":"0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","name":"Work","color":"#3b6ea5","description":"","timezone":"Europe/Moscow","kind":"local","read_only":false,"is_default":false,"hidden":false,"position":1,"default_reminders":[10],"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T14:02:11Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/calendars/{id}":{"parameters":[{"$ref":"#/components/parameters/CalendarID"}],"get":{"tags":["Calendars"],"operationId":"getCalendar","summary":"Get a calendar","description":"Returns one calendar. Send the `ETag` from an earlier response in `If-None-Match` to get `304` when nothing changed.\n\nRequires scope `calendars:read`.\n","x-scope":"calendars:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"The calendar.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Calendar"},"example":{"id":"0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","name":"Work","color":"#3b6ea5","description":"","timezone":"Europe/Moscow","kind":"local","read_only":false,"is_default":false,"hidden":false,"position":1,"default_reminders":[10],"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T14:02:11Z"}}}},"304":{"$ref":"#/components/responses/NotModified"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"patch":{"tags":["Calendars"],"operationId":"updateCalendar","summary":"Update a calendar","description":"Changes the fields present in the body as a JSON Merge Patch. Send `\"timezone\": null` to fall back to the account timezone. `kind`, `read_only` and `is_default` cannot be changed.\n\nRequires scope `calendars:write`.\n","x-scope":"calendars:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"}],"requestBody":{"required":true,"content":{"application/merge-patch+json":{"schema":{"$ref":"#/components/schemas/CalendarInput"},"example":{"name":"Personal","timezone":null}},"application/json":{"schema":{"$ref":"#/components/schemas/CalendarInput"},"example":{"hidden":true}}}},"responses":{"200":{"description":"The updated calendar.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Calendar"},"example":{"id":"0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","name":"Work","color":"#3b6ea5","description":"","timezone":"Europe/Moscow","kind":"local","read_only":false,"is_default":false,"hidden":false,"position":1,"default_reminders":[10],"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T14:02:11Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"delete":{"tags":["Calendars"],"operationId":"deleteCalendar","summary":"Delete a calendar","description":"Deletes the calendar together with all of its events and removes it from share links. The default calendar cannot be deleted.\n\nRequires scope `calendars:write`.\n","x-scope":"calendars:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"}],"responses":{"204":{"description":"The calendar and its events were deleted.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/events":{"get":{"tags":["Events"],"operationId":"listEvents","summary":"List events in a time range","description":"Returns events that overlap the range from `from` to `to`, ordered by start. The range can be at most 400 days. All-day events are matched by date in the account timezone.\n\nRecurring series are expanded into occurrences. An occurrence of a series carries the series `id` and its original start in `instance`. An occurrence changed on its own has its own `id`, the series in `series_id` and its original start in `instance`. With `expand=false` the stored series, changed occurrences and single events are returned without expansion.\n\nRequires scope `calendars:read`.\n","x-scope":"calendars:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"$ref":"#/components/parameters/RangeFrom"},{"$ref":"#/components/parameters/RangeTo"},{"$ref":"#/components/parameters/CalendarFilter"},{"$ref":"#/components/parameters/Expand"}],"responses":{"200":{"description":"Events and occurrences in the range.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventList"},"example":{"items":[{"id":"0191f2c5-1b2c-7d4e-8f90-a1b2c3d4e5f6","calendar_id":"0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","uid":"0191f2c5-1b2c-7d4e-8f90-a1b2c3d4e5f6","series_id":null,"instance":"2026-09-11T10:00:00+03:00","recurring":true,"title":"Standup","body":"Agenda and **notes**","location":"Office","url":"","all_day":false,"start":"2026-09-11T10:00:00+03:00","end":"2026-09-11T10:15:00+03:00","timezone":"Europe/Moscow","rrule":"FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR","rdate":[],"exdate":["2026-09-14T10:00:00+03:00"],"status":"confirmed","transparency":"opaque","visibility":"default","color":null,"read_only":false,"reminders":[10],"version":3,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T14:02:11Z"},{"id":"0191f2c6-2d3e-7f40-8a51-b2c3d4e5f607","calendar_id":"0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","uid":"0191f2c6-2d3e-7f40-8a51-b2c3d4e5f607","series_id":null,"instance":null,"recurring":false,"title":"Conference","body":"","location":"Vilnius","url":"https://example.com/conf","all_day":true,"start":"2026-09-20","end":"2026-09-22","timezone":null,"rrule":null,"rdate":[],"exdate":[],"status":"tentative","transparency":"transparent","visibility":"private","color":"#aa3355","read_only":false,"reminders":[],"version":1,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T13:56:42Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"post":{"tags":["Events"],"operationId":"createEvent","summary":"Create an event","description":"Creates an event. Only `start` is required. Without `calendar_id` the event goes to the default calendar. Without `end` a timed event lasts one hour and an all-day event one day. Times without an offset are read in `timezone`, which defaults to the calendar timezone and then the account timezone. Without `reminders` the calendar's default reminders are used.\n\nRequires scope `calendars:write`.\n","x-scope":"calendars:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventInput"},"examples":{"recurring":{"summary":"Weekday standup","value":{"title":"Standup","start":"2026-09-11T10:00:00+03:00","end":"2026-09-11T10:15:00+03:00","timezone":"Europe/Moscow","rrule":"FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR","reminders":[10]}},"allDay":{"summary":"All-day event","value":{"title":"Conference","all_day":true,"start":"2026-09-20","end":"2026-09-22"}}}}}},"responses":{"201":{"description":"The created event.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Location":{"$ref":"#/components/headers/Location"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"},"example":{"id":"0191f2c5-1b2c-7d4e-8f90-a1b2c3d4e5f6","calendar_id":"0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","uid":"0191f2c5-1b2c-7d4e-8f90-a1b2c3d4e5f6","series_id":null,"instance":"2026-09-11T10:00:00+03:00","recurring":true,"title":"Standup","body":"Agenda and **notes**","location":"Office","url":"","all_day":false,"start":"2026-09-11T10:00:00+03:00","end":"2026-09-11T10:15:00+03:00","timezone":"Europe/Moscow","rrule":"FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR","rdate":[],"exdate":["2026-09-14T10:00:00+03:00"],"status":"confirmed","transparency":"opaque","visibility":"default","color":null,"read_only":false,"reminders":[10],"version":3,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T14:02:11Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/events/{id}":{"parameters":[{"$ref":"#/components/parameters/EventID"}],"get":{"tags":["Events"],"operationId":"getEvent","summary":"Get an event","description":"Returns one stored event: a single event, a recurring series or an occurrence changed on its own. Use `GET /events/{id}/instances` for the occurrences of a series.\n\nRequires scope `calendars:read`.\n","x-scope":"calendars:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"The event.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"},"example":{"id":"0191f2c5-1b2c-7d4e-8f90-a1b2c3d4e5f6","calendar_id":"0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","uid":"0191f2c5-1b2c-7d4e-8f90-a1b2c3d4e5f6","series_id":null,"instance":"2026-09-11T10:00:00+03:00","recurring":true,"title":"Standup","body":"Agenda and **notes**","location":"Office","url":"","all_day":false,"start":"2026-09-11T10:00:00+03:00","end":"2026-09-11T10:15:00+03:00","timezone":"Europe/Moscow","rrule":"FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR","rdate":[],"exdate":["2026-09-14T10:00:00+03:00"],"status":"confirmed","transparency":"opaque","visibility":"default","color":null,"read_only":false,"reminders":[10],"version":3,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T14:02:11Z"}}}},"304":{"$ref":"#/components/responses/NotModified"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"patch":{"tags":["Events"],"operationId":"updateEvent","summary":"Update an event","description":"Changes the fields present in the body as a JSON Merge Patch. Changing `start` without `end` keeps the duration.\n\nFor a recurring series, `scope` and `instance` choose what changes:\n\n- `this` changes one occurrence and returns it as a changed occurrence.\n- `following` ends the series before the occurrence and starts a new series there with the changes applied. The response is the new series. Its `uid` is the old one with `_R` and the occurrence start in UTC appended, as in `\u003cuid\u003e_R20260911T100000Z`, or a fresh ID when that would pass 1000 characters. Changed occurrences, extra dates and excluded dates at or after the split move to the new series.\n- `all` applies the fields to the series itself, so `start` sets the first occurrence. Moving the start also moves excluded dates, extra dates and changed occurrences by the same amount, unless the request sets `rdate` or `exdate` itself, or changes `all_day` or `rrule`.\n\nA changed occurrence that no longer falls on the rule is deleted.\n\nRecurrence fields can only change with `following` or `all`. `uid` cannot be changed.\n\nRequires scope `calendars:write`.\n","x-scope":"calendars:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"parameters":[{"$ref":"#/components/parameters/EditScope"},{"$ref":"#/components/parameters/Instance"},{"$ref":"#/components/parameters/IfMatch"}],"requestBody":{"required":true,"content":{"application/merge-patch+json":{"schema":{"$ref":"#/components/schemas/EventInput"},"example":{"start":"2026-09-11T11:00:00+03:00"}},"application/json":{"schema":{"$ref":"#/components/schemas/EventInput"},"example":{"title":"Standup moved","location":null}}}},"responses":{"200":{"description":"The changed event, changed occurrence or new series.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"},"example":{"id":"0191f2c5-1b2c-7d4e-8f90-a1b2c3d4e5f6","calendar_id":"0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","uid":"0191f2c5-1b2c-7d4e-8f90-a1b2c3d4e5f6","series_id":null,"instance":"2026-09-11T10:00:00+03:00","recurring":true,"title":"Standup","body":"Agenda and **notes**","location":"Office","url":"","all_day":false,"start":"2026-09-11T10:00:00+03:00","end":"2026-09-11T10:15:00+03:00","timezone":"Europe/Moscow","rrule":"FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR","rdate":[],"exdate":["2026-09-14T10:00:00+03:00"],"status":"confirmed","transparency":"opaque","visibility":"default","color":null,"read_only":false,"reminders":[10],"version":3,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T14:02:11Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"delete":{"tags":["Events"],"operationId":"deleteEvent","summary":"Delete an event","description":"Deletes the event and moves it to the trash. For a recurring series, `this` excludes one occurrence and deletes the changed copy of it if there is one, `following` ends the series before the occurrence, and `all` deletes the series with every changed occurrence.\n\nRequires scope `calendars:write`.\n","x-scope":"calendars:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"parameters":[{"$ref":"#/components/parameters/EditScope"},{"$ref":"#/components/parameters/Instance"},{"$ref":"#/components/parameters/IfMatch"}],"responses":{"204":{"description":"The event or occurrences were deleted.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/events/{id}/instances":{"parameters":[{"$ref":"#/components/parameters/EventID"}],"get":{"tags":["Events"],"operationId":"listEventInstances","summary":"List occurrences of an event","description":"Returns the occurrences of one event that overlap the range, including occurrences changed on their own. A single event returns itself when it overlaps the range.\n\nRequires scope `calendars:read`.\n","x-scope":"calendars:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"$ref":"#/components/parameters/RangeFrom"},{"$ref":"#/components/parameters/RangeTo"}],"responses":{"200":{"description":"Occurrences in the range.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventList"},"example":{"items":[{"id":"0191f2c5-1b2c-7d4e-8f90-a1b2c3d4e5f6","calendar_id":"0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","uid":"0191f2c5-1b2c-7d4e-8f90-a1b2c3d4e5f6","series_id":null,"instance":"2026-09-11T10:00:00+03:00","recurring":true,"title":"Standup","body":"Agenda and **notes**","location":"Office","url":"","all_day":false,"start":"2026-09-11T10:00:00+03:00","end":"2026-09-11T10:15:00+03:00","timezone":"Europe/Moscow","rrule":"FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR","rdate":[],"exdate":["2026-09-14T10:00:00+03:00"],"status":"confirmed","transparency":"opaque","visibility":"default","color":null,"read_only":false,"reminders":[10],"version":3,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T14:02:11Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/todo-lists":{"get":{"tags":["Todo lists"],"operationId":"listTodoLists","summary":"List todo lists","description":"Returns every todo list of the account ordered by `position`.\n\nRequires scope `todos:read`.\n","x-scope":"todos:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"200":{"description":"All todo lists of the account.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodoListList"},"example":{"items":[{"id":"0191f2c7-3e4f-7a51-9b62-c3d4e5f60718","name":"Inbox","color":"#5b7c5a","position":0,"is_default":true,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T13:56:42Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"post":{"tags":["Todo lists"],"operationId":"createTodoList","summary":"Create a todo list","description":"Creates a todo list. Only `name` is required. The list is placed after the others unless `position` is set. An account can have at most 100 lists.\n\nRequires scope `todos:write`.\n","x-scope":"todos:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodoListInput"},"example":{"name":"Work","color":"#3b6ea5"}}}},"responses":{"201":{"description":"The created list.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Location":{"$ref":"#/components/headers/Location"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodoList"},"example":{"id":"0191f2c7-3e4f-7a51-9b62-c3d4e5f60718","name":"Inbox","color":"#5b7c5a","position":0,"is_default":true,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T13:56:42Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/todo-lists/{id}":{"parameters":[{"$ref":"#/components/parameters/TodoListID"}],"get":{"tags":["Todo lists"],"operationId":"getTodoList","summary":"Get a todo list","description":"Returns one todo list.\n\nRequires scope `todos:read`.\n","x-scope":"todos:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"The list.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodoList"},"example":{"id":"0191f2c7-3e4f-7a51-9b62-c3d4e5f60718","name":"Inbox","color":"#5b7c5a","position":0,"is_default":true,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T13:56:42Z"}}}},"304":{"$ref":"#/components/responses/NotModified"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"patch":{"tags":["Todo lists"],"operationId":"updateTodoList","summary":"Update a todo list","description":"Changes the fields present in the body as a JSON Merge Patch.\n\nRequires scope `todos:write`.\n","x-scope":"todos:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"}],"requestBody":{"required":true,"content":{"application/merge-patch+json":{"schema":{"$ref":"#/components/schemas/TodoListInput"},"example":{"name":"Errands"}},"application/json":{"schema":{"$ref":"#/components/schemas/TodoListInput"},"example":{"position":2}}}},"responses":{"200":{"description":"The updated list.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodoList"},"example":{"id":"0191f2c7-3e4f-7a51-9b62-c3d4e5f60718","name":"Inbox","color":"#5b7c5a","position":0,"is_default":true,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T13:56:42Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"delete":{"tags":["Todo lists"],"operationId":"deleteTodoList","summary":"Delete a todo list","description":"Deletes the list together with all of its todos. The default list cannot be deleted.\n\nRequires scope `todos:write`.\n","x-scope":"todos:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"}],"responses":{"204":{"description":"The list and its todos were deleted.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/todos":{"get":{"tags":["Todos"],"operationId":"listTodos","summary":"List todos","description":"Returns todos in creation order, open todos by default. Subtasks are included; filter them with `parent_id`. Results come in pages of at most `limit` items. Pass `next_cursor` as `cursor` to get the next page, it is null on the last page.\n\nRequires scope `todos:read`.\n","x-scope":"todos:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"$ref":"#/components/parameters/TodoListFilter"},{"$ref":"#/components/parameters/ParentFilter"},{"$ref":"#/components/parameters/TodoStatus"},{"$ref":"#/components/parameters/DueFrom"},{"$ref":"#/components/parameters/DueTo"},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"One page of todos.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodoPage"},"example":{"items":[{"id":"0191f2c8-4f50-7b62-8c73-d4e5f6071829","list_id":"0191f2c7-3e4f-7a51-9b62-c3d4e5f60718","parent_id":null,"title":"Assemble GPU node","body":"Check PSU rails first","status":"needs_action","priority":2,"position":"V","due_date":"2026-09-12","due_time":"18:00","duration_min":90,"timezone":"Europe/Moscow","rrule":null,"show_on_calendar":true,"completed_at":null,"reminders":[30],"checks":[{"id":"0191f2c9-5061-7c73-9d84-e5f607182930","text":"Riser cables","done":true,"position":"V","done_at":"2026-09-10T14:02:11Z"},{"id":"0191f2c9-5061-7c73-9d84-e5f607182931","text":"Thermal paste","done":false,"position":"l","done_at":null}],"version":2,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T14:02:11Z"}],"next_cursor":null}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"post":{"tags":["Todos"],"operationId":"createTodo","summary":"Create a todo","description":"Creates a todo. Only `title` is required. Without `list_id` the todo goes to the default list, or to the list of `parent_id`. `due_time` needs `due_date`, and `timezone` defaults to the account timezone. `show_on_calendar` defaults to true when a time is set. `checks` can only be set here, change them later with the checks endpoints.\n\nRequires scope `todos:write`.\n","x-scope":"todos:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodoInput"},"example":{"title":"Assemble GPU node","due_date":"2026-09-12","due_time":"18:00","duration_min":90,"priority":2,"checks":[{"text":"Riser cables"},{"text":"Thermal paste"}]}}}},"responses":{"201":{"description":"The created todo.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Location":{"$ref":"#/components/headers/Location"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Todo"},"example":{"id":"0191f2c8-4f50-7b62-8c73-d4e5f6071829","list_id":"0191f2c7-3e4f-7a51-9b62-c3d4e5f60718","parent_id":null,"title":"Assemble GPU node","body":"Check PSU rails first","status":"needs_action","priority":2,"position":"V","due_date":"2026-09-12","due_time":"18:00","duration_min":90,"timezone":"Europe/Moscow","rrule":null,"show_on_calendar":true,"completed_at":null,"reminders":[30],"checks":[{"id":"0191f2c9-5061-7c73-9d84-e5f607182930","text":"Riser cables","done":true,"position":"V","done_at":"2026-09-10T14:02:11Z"},{"id":"0191f2c9-5061-7c73-9d84-e5f607182931","text":"Thermal paste","done":false,"position":"l","done_at":null}],"version":2,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T14:02:11Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/todos/{id}":{"parameters":[{"$ref":"#/components/parameters/TodoID"}],"get":{"tags":["Todos"],"operationId":"getTodo","summary":"Get a todo","description":"Returns one todo with its checklist.\n\nRequires scope `todos:read`.\n","x-scope":"todos:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"The todo.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Todo"},"example":{"id":"0191f2c8-4f50-7b62-8c73-d4e5f6071829","list_id":"0191f2c7-3e4f-7a51-9b62-c3d4e5f60718","parent_id":null,"title":"Assemble GPU node","body":"Check PSU rails first","status":"needs_action","priority":2,"position":"V","due_date":"2026-09-12","due_time":"18:00","duration_min":90,"timezone":"Europe/Moscow","rrule":null,"show_on_calendar":true,"completed_at":null,"reminders":[30],"checks":[{"id":"0191f2c9-5061-7c73-9d84-e5f607182930","text":"Riser cables","done":true,"position":"V","done_at":"2026-09-10T14:02:11Z"},{"id":"0191f2c9-5061-7c73-9d84-e5f607182931","text":"Thermal paste","done":false,"position":"l","done_at":null}],"version":2,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T14:02:11Z"}}}},"304":{"$ref":"#/components/responses/NotModified"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"patch":{"tags":["Todos"],"operationId":"updateTodo","summary":"Update a todo","description":"Changes the fields present in the body as a JSON Merge Patch. Clearing `due_date` also clears the time, duration and recurrence. Moving a todo to another list moves its subtasks with it; a subtask moves only by changing `parent_id`. Use the complete and reopen endpoints to change the status.\n\nRequires scope `todos:write`.\n","x-scope":"todos:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"}],"requestBody":{"required":true,"content":{"application/merge-patch+json":{"schema":{"$ref":"#/components/schemas/TodoInput"},"example":{"due_time":null}},"application/json":{"schema":{"$ref":"#/components/schemas/TodoInput"},"example":{"priority":3}}}},"responses":{"200":{"description":"The updated todo.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Todo"},"example":{"id":"0191f2c8-4f50-7b62-8c73-d4e5f6071829","list_id":"0191f2c7-3e4f-7a51-9b62-c3d4e5f60718","parent_id":null,"title":"Assemble GPU node","body":"Check PSU rails first","status":"needs_action","priority":2,"position":"V","due_date":"2026-09-12","due_time":"18:00","duration_min":90,"timezone":"Europe/Moscow","rrule":null,"show_on_calendar":true,"completed_at":null,"reminders":[30],"checks":[{"id":"0191f2c9-5061-7c73-9d84-e5f607182930","text":"Riser cables","done":true,"position":"V","done_at":"2026-09-10T14:02:11Z"},{"id":"0191f2c9-5061-7c73-9d84-e5f607182931","text":"Thermal paste","done":false,"position":"l","done_at":null}],"version":2,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T14:02:11Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"delete":{"tags":["Todos"],"operationId":"deleteTodo","summary":"Delete a todo","description":"Deletes the todo and its subtasks and moves them to the trash.\n\nRequires scope `todos:write`.\n","x-scope":"todos:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"}],"responses":{"204":{"description":"The todo and its subtasks were deleted.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/todos/{id}/complete":{"parameters":[{"$ref":"#/components/parameters/TodoID"}],"post":{"tags":["Todos"],"operationId":"completeTodo","summary":"Complete a todo","description":"Marks the todo as completed. Checklist items are left as they are. For a recurring todo the next occurrence is created with every checklist item unchecked, and its ID is returned in `next_id`. Completing a completed todo changes nothing.\n\nRequires scope `todos:write`.\n","x-scope":"todos:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"}],"responses":{"200":{"description":"The completed todo and the next occurrence, if any.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodoCompletion"},"example":{"todo":{"id":"0191f2c8-4f50-7b62-8c73-d4e5f6071829","list_id":"0191f2c7-3e4f-7a51-9b62-c3d4e5f60718","parent_id":null,"title":"Assemble GPU node","body":"Check PSU rails first","status":"needs_action","priority":2,"position":"V","due_date":"2026-09-12","due_time":"18:00","duration_min":90,"timezone":"Europe/Moscow","rrule":null,"show_on_calendar":true,"completed_at":null,"reminders":[30],"checks":[{"id":"0191f2c9-5061-7c73-9d84-e5f607182930","text":"Riser cables","done":true,"position":"V","done_at":"2026-09-10T14:02:11Z"},{"id":"0191f2c9-5061-7c73-9d84-e5f607182931","text":"Thermal paste","done":false,"position":"l","done_at":null}],"version":2,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T14:02:11Z"},"next_id":"0191f2ca-6172-7d84-8e95-f60718293041"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/todos/{id}/reopen":{"parameters":[{"$ref":"#/components/parameters/TodoID"}],"post":{"tags":["Todos"],"operationId":"reopenTodo","summary":"Reopen a todo","description":"Marks a completed todo as open again. A next occurrence created on completion is kept.\n\nRequires scope `todos:write`.\n","x-scope":"todos:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"}],"responses":{"200":{"description":"The reopened todo.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Todo"},"example":{"id":"0191f2c8-4f50-7b62-8c73-d4e5f6071829","list_id":"0191f2c7-3e4f-7a51-9b62-c3d4e5f60718","parent_id":null,"title":"Assemble GPU node","body":"Check PSU rails first","status":"needs_action","priority":2,"position":"V","due_date":"2026-09-12","due_time":"18:00","duration_min":90,"timezone":"Europe/Moscow","rrule":null,"show_on_calendar":true,"completed_at":null,"reminders":[30],"checks":[{"id":"0191f2c9-5061-7c73-9d84-e5f607182930","text":"Riser cables","done":true,"position":"V","done_at":"2026-09-10T14:02:11Z"},{"id":"0191f2c9-5061-7c73-9d84-e5f607182931","text":"Thermal paste","done":false,"position":"l","done_at":null}],"version":2,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T14:02:11Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/todos/{id}/move":{"parameters":[{"$ref":"#/components/parameters/TodoID"}],"post":{"tags":["Todos"],"operationId":"moveTodo","summary":"Move a todo","description":"Moves the todo to another list or parent, and places it right after `after_id`. With `after_id` null it goes first; without it, it goes last when the list or parent changes and stays in place otherwise.\n\nRequires scope `todos:write`.\n","x-scope":"todos:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodoMove"},"example":{"list_id":"0191f2c7-3e4f-7a51-9b62-c3d4e5f60718","after_id":null}}}},"responses":{"200":{"description":"The moved todo.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Todo"},"example":{"id":"0191f2c8-4f50-7b62-8c73-d4e5f6071829","list_id":"0191f2c7-3e4f-7a51-9b62-c3d4e5f60718","parent_id":null,"title":"Assemble GPU node","body":"Check PSU rails first","status":"needs_action","priority":2,"position":"V","due_date":"2026-09-12","due_time":"18:00","duration_min":90,"timezone":"Europe/Moscow","rrule":null,"show_on_calendar":true,"completed_at":null,"reminders":[30],"checks":[{"id":"0191f2c9-5061-7c73-9d84-e5f607182930","text":"Riser cables","done":true,"position":"V","done_at":"2026-09-10T14:02:11Z"},{"id":"0191f2c9-5061-7c73-9d84-e5f607182931","text":"Thermal paste","done":false,"position":"l","done_at":null}],"version":2,"created_at":"2026-09-10T13:56:42Z","updated_at":"2026-09-10T14:02:11Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/todos/{id}/checks":{"parameters":[{"$ref":"#/components/parameters/TodoID"}],"get":{"tags":["Checks"],"operationId":"listChecks","summary":"List checklist items","description":"Returns the checklist of a todo in order.\n\nRequires scope `todos:read`.\n","x-scope":"todos:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"200":{"description":"The checklist.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckList"},"example":{"items":[{"id":"0191f2c9-5061-7c73-9d84-e5f607182930","text":"Riser cables","done":true,"position":"V","done_at":"2026-09-10T14:02:11Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"post":{"tags":["Checks"],"operationId":"createCheck","summary":"Add a checklist item","description":"Adds an item at the end of the checklist. A todo can have at most 200 items.\n\nRequires scope `todos:write`.\n","x-scope":"todos:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckInput"},"example":{"text":"Order fans"}}}},"responses":{"201":{"description":"The added item.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Check"},"example":{"id":"0191f2c9-5061-7c73-9d84-e5f607182930","text":"Riser cables","done":true,"position":"V","done_at":"2026-09-10T14:02:11Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"put":{"tags":["Checks"],"operationId":"reorderChecks","summary":"Reorder checklist items","description":"Sets the order of the checklist. `ids` must list every item exactly once.\n\nRequires scope `todos:write`.\n","x-scope":"todos:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckOrder"},"example":{"ids":["0191f2c9-5061-7c73-9d84-e5f607182931","0191f2c9-5061-7c73-9d84-e5f607182930"]}}}},"responses":{"200":{"description":"The checklist in the new order.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckList"},"example":{"items":[{"id":"0191f2c9-5061-7c73-9d84-e5f607182930","text":"Riser cables","done":true,"position":"V","done_at":"2026-09-10T14:02:11Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/todos/{id}/checks/{cid}":{"parameters":[{"$ref":"#/components/parameters/TodoID"},{"$ref":"#/components/parameters/CheckID"}],"patch":{"tags":["Checks"],"operationId":"updateCheck","summary":"Update a checklist item","description":"Changes the text or checks and unchecks the item. Checking every item does not complete the todo.\n\nRequires scope `todos:write`.\n","x-scope":"todos:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"requestBody":{"required":true,"content":{"application/merge-patch+json":{"schema":{"$ref":"#/components/schemas/CheckInput"},"example":{"done":true}},"application/json":{"schema":{"$ref":"#/components/schemas/CheckInput"},"example":{"text":"Thermal paste, the good one"}}}},"responses":{"200":{"description":"The updated item.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Check"},"example":{"id":"0191f2c9-5061-7c73-9d84-e5f607182930","text":"Riser cables","done":true,"position":"V","done_at":"2026-09-10T14:02:11Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"delete":{"tags":["Checks"],"operationId":"deleteCheck","summary":"Delete a checklist item","description":"Removes the item from the checklist.\n\nRequires scope `todos:write`.\n","x-scope":"todos:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"responses":{"204":{"description":"The item was removed.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/imports":{"get":{"tags":["Imports"],"operationId":"listImports","summary":"List recent imports","description":"Returns recent imports without their previews, newest first.\n\nRequires scope `imports:write`.\n","x-scope":"imports:write","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"$ref":"#/components/parameters/ImportLimit"}],"responses":{"200":{"description":"Recent imports.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportList"},"example":{"items":[{"id":"0191f2d0-1111-7000-8000-a1b2c3d4e5f6","source":"ics","filename":"work.ics","status":"previewed","preview":null,"stats":null,"created_at":"2026-09-10T13:56:42Z","expires_at":"2026-09-11T13:56:42Z","finished_at":null}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"post":{"tags":["Imports"],"operationId":"createImport","summary":"Upload a file","description":"Uploads an `.ics`, `.csv` or `.yaml` file and returns what it holds. Nothing is written until the import is committed. The uploaded file is kept for 24 hours.\n\nThe preview lists a target per calendar and list found in the file, each with an `index` to send back on commit.\n\nRequires scope `imports:write`.\n","x-scope":"imports:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","additionalProperties":false,"required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"The file to import."}}},"encoding":{"file":{"contentType":"text/calendar, text/csv, application/yaml"}}}}},"responses":{"201":{"description":"The import with its preview.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Location":{"$ref":"#/components/headers/Location"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Import"},"example":{"id":"0191f2d0-1111-7000-8000-a1b2c3d4e5f6","source":"ics","filename":"work.ics","status":"previewed","preview":null,"stats":null,"created_at":"2026-09-10T13:56:42Z","expires_at":"2026-09-11T13:56:42Z","finished_at":null}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/imports/{id}":{"parameters":[{"$ref":"#/components/parameters/ImportID"}],"get":{"tags":["Imports"],"operationId":"getImport","summary":"Get an import","description":"Returns one import with its preview, and its result once committed.\n\nRequires scope `imports:write`.\n","x-scope":"imports:write","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"200":{"description":"The import.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Import"},"example":{"id":"0191f2d0-1111-7000-8000-a1b2c3d4e5f6","source":"ics","filename":"work.ics","status":"previewed","preview":null,"stats":null,"created_at":"2026-09-10T13:56:42Z","expires_at":"2026-09-11T13:56:42Z","finished_at":null}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/imports/{id}/commit":{"parameters":[{"$ref":"#/components/parameters/ImportID"}],"post":{"tags":["Imports"],"operationId":"commitImport","summary":"Write an import in","description":"Writes the file in. Each target takes the calendar or list ID to import into, or `new` to create one. Targets left out keep the choice from the preview.\n\nEntries that match an existing one by UID are updated. Entries with the same title, time and place as one already there are skipped. An import can only be committed once.\n\nRequires scope `imports:write`.\n","x-scope":"imports:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportCommit"},"example":{"targets":[{"index":0,"target":"0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10"},{"index":1,"skip":true}]}}}},"responses":{"200":{"description":"The finished import with its result.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Import"},"example":{"id":"0191f2d0-1111-7000-8000-a1b2c3d4e5f6","source":"ics","filename":"work.ics","status":"previewed","preview":null,"stats":null,"created_at":"2026-09-10T13:56:42Z","expires_at":"2026-09-11T13:56:42Z","finished_at":null}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/exports/ics":{"get":{"tags":["Exports"],"operationId":"exportICS","summary":"Download calendars as iCalendar","description":"Returns an iCalendar document with the chosen calendars, their recurring series and changed occurrences. Without `calendar_id` every calendar is included.\n\nRequires scope `exports:read`.\n","x-scope":"exports:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"$ref":"#/components/parameters/ExportCalendars"}],"responses":{"200":{"description":"An iCalendar document.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"text/calendar":{"schema":{"$ref":"#/components/schemas/ICSDocument"},"example":"BEGIN:VCALENDAR\nVERSION:2.0\nEND:VCALENDAR\n"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/exports/csv":{"get":{"tags":["Exports"],"operationId":"exportCSV","summary":"Download calendars as CSV","description":"Returns one row per event, with the columns Google and Outlook use. Recurring series appear once, as the rule rather than its occurrences.\n\nRequires scope `exports:read`.\n","x-scope":"exports:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"$ref":"#/components/parameters/ExportCalendars"}],"responses":{"200":{"description":"A CSV file.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"text/csv":{"schema":{"$ref":"#/components/schemas/CSVDocument"},"example":"Subject,Start Date\nStandup,2026-09-07\n"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/exports/account.yaml":{"get":{"tags":["Exports"],"operationId":"exportAccount","summary":"Download the whole account","description":"Returns every calendar, event, todo list, todo, checklist and the account settings in this app's own YAML format. It imports back with nothing lost.\n\nRequires scope `exports:read`.\n","x-scope":"exports:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"200":{"description":"A YAML document.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/yaml":{"schema":{"$ref":"#/components/schemas/AccountYAML"},"example":"format: calendar\nschema: 1\n"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/subscriptions":{"get":{"tags":["Subscriptions"],"operationId":"listSubscriptions","summary":"List subscriptions","description":"Returns followed ICS feeds with their refresh status.\n\nRequires scope `calendars:read`.\n","x-scope":"calendars:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"200":{"description":"All subscriptions of the account.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionList"},"example":{"items":[{"id":"0191f2d1-2222-7000-8000-a1b2c3d4e5f6","calendar_id":"0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","url":"https://example.com/calendar.ics","refresh_every":"1h0m0s","status":"ok","next_refresh_at":"2026-09-10T14:56:42Z","last_ok_at":"2026-09-10T13:56:42Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"post":{"tags":["Subscriptions"],"operationId":"createSubscription","summary":"Follow an ICS feed","description":"Creates a read-only calendar filled from the feed and refreshed on its own. `http`, `https` and `webcal` links are accepted; addresses that resolve to private networks are refused.\n\nRequires scope `calendars:write`.\n","x-scope":"calendars:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionInput"},"example":{"url":"https://example.com/calendar.ics","name":"Team","refresh_every":"1h"}}}},"responses":{"201":{"description":"The created subscription.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Location":{"$ref":"#/components/headers/Location"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"},"example":{"id":"0191f2d1-2222-7000-8000-a1b2c3d4e5f6","calendar_id":"0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","url":"https://example.com/calendar.ics","refresh_every":"1h0m0s","status":"ok","next_refresh_at":"2026-09-10T14:56:42Z","last_ok_at":"2026-09-10T13:56:42Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/subscriptions/{id}":{"parameters":[{"$ref":"#/components/parameters/SubscriptionID"}],"delete":{"tags":["Subscriptions"],"operationId":"deleteSubscription","summary":"Stop following a feed","description":"Removes the subscription together with its calendar and every event it brought in.\n\nRequires scope `calendars:write`.\n","x-scope":"calendars:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"responses":{"204":{"description":"The subscription and its calendar were removed.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/subscriptions/{id}/refresh":{"parameters":[{"$ref":"#/components/parameters/SubscriptionID"}],"post":{"tags":["Subscriptions"],"operationId":"refreshSubscription","summary":"Refresh a feed now","description":"Queues a refresh and clears any failure state. The feed is fetched in the background.\n\nRequires scope `calendars:write`.\n","x-scope":"calendars:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"responses":{"202":{"description":"The refresh was queued.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/duplicates":{"get":{"tags":["Duplicates"],"operationId":"listDuplicates","summary":"List duplicate pairs","description":"Returns pairs that look like copies of each other, newest first. Each side carries a snapshot taken when the pair was found, so it reads the same after one side is deleted.\n\nRequires scope `calendars:read`.\n","x-scope":"calendars:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"$ref":"#/components/parameters/DuplicateStatus"}],"responses":{"200":{"description":"Duplicate pairs.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DuplicateList"},"example":{"items":[{"id":"0191f2e0-1111-7000-8000-a1b2c3d4e5f6","entity":"event","reason":"fingerprint","score":1,"status":"pending","a":{"id":"0191f2c5-1b2c-7d4e-8f90-a1b2c3d4e5f6","title":"Standup","when":"2026-09-07 10:00","where":"","created":"2026-09-01T09:00:00Z"},"b":{"id":"0191f2c6-2d3e-7f40-8a51-b2c3d4e5f607","title":"Standup","when":"2026-09-07 10:00","where":"","created":"2026-09-02T09:00:00Z"},"created_at":"2026-09-10T13:56:42Z","resolved_at":null}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/duplicates/scan":{"post":{"tags":["Duplicates"],"operationId":"scanDuplicates","summary":"Look for duplicates now","description":"Queues a scan of events around today and open todos. New pairs appear in the list when it finishes.\n\nRequires scope `calendars:write`.\n","x-scope":"calendars:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"responses":{"202":{"description":"The scan was queued.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/duplicates/{id}/resolve":{"parameters":[{"$ref":"#/components/parameters/DuplicateID"}],"post":{"tags":["Duplicates"],"operationId":"resolveDuplicate","summary":"Resolve a duplicate pair","description":"`merge` fills empty fields of the first entry from the second, unions todo checklists, and moves the second to the trash. `keep_a` and `keep_b` move the other one to the trash. `dismiss` keeps both and stops the pair being flagged again.\n\nRequires scope `calendars:write`.\n","x-scope":"calendars:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DuplicateResolve"},"example":{"action":"merge"}}}},"responses":{"204":{"description":"The pair was resolved.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/trash":{"get":{"tags":["Trash"],"operationId":"listTrash","summary":"List deleted entries","description":"Returns deleted events and todos, newest first. Changed occurrences and subtasks are counted on their parent rather than listed.\n\nRequires scope `calendars:read`.\n","x-scope":"calendars:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"$ref":"#/components/parameters/TrashLimit"}],"responses":{"200":{"description":"Deleted entries.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrashList"},"example":{"items":[{"id":"0191f2c5-1b2c-7d4e-8f90-a1b2c3d4e5f6","entity":"event","title":"Standup","when":"2026-09-07 10:00","where":"Work","children":2,"deleted_at":"2026-09-10T13:56:42Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/trash/{entity}/{id}":{"parameters":[{"$ref":"#/components/parameters/TrashEntity"},{"$ref":"#/components/parameters/TrashID"}],"delete":{"tags":["Trash"],"operationId":"purgeTrash","summary":"Delete for good","description":"Removes the entry and everything deleted with it. This cannot be undone.\n\nRequires scope `calendars:write`.\n","x-scope":"calendars:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"responses":{"204":{"description":"The entry was removed.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/trash/{entity}/{id}/restore":{"parameters":[{"$ref":"#/components/parameters/TrashEntity"},{"$ref":"#/components/parameters/TrashID"}],"post":{"tags":["Trash"],"operationId":"restoreTrash","summary":"Restore a deleted entry","description":"Brings the entry back, together with the occurrences or subtasks deleted with it.\n\nRequires scope `calendars:write`.\n","x-scope":"calendars:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"responses":{"204":{"description":"The entry was restored.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/search":{"get":{"tags":["Search"],"operationId":"search","summary":"Search events and todos","description":"Finds events and todos by text, ranked by how well they match and how close they are to today.\n\nThe query accepts `\"exact phrase\"` and `-exclude`, plus filters:\n\n| Filter | Matches |\n|---|---|\n| `in:\u003cname\u003e` | that calendar or list |\n| `type:event`, `type:todo` | one kind |\n| `is:open`, `is:done`, `is:overdue` | todo status |\n| `is:recurring`, `is:private` | repeating entries, private events |\n| `has:time`, `has:checks`, `has:body`, `has:location`, `has:reminders` | entries with that |\n| `after:`, `before:` | a date like `2026-09-10`, `today`, or an offset like `+7d` |\n| `priority:\u003e=2` | todos at that priority |\n\nA filter that cannot apply to a kind excludes that kind, so `is:done` returns todos only. Filters that were not understood are reported in `warnings` and used as plain words.\n\nRecurring events match once, as the series.\n\nRequires scope `calendars:read`.\n","x-scope":"calendars:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"$ref":"#/components/parameters/SearchQuery"},{"$ref":"#/components/parameters/SearchType"},{"$ref":"#/components/parameters/SearchLimit"},{"$ref":"#/components/parameters/SearchOffset"}],"responses":{"200":{"description":"Matching entries, best first.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResult"},"example":{"query":"standup in:Work","type":"","items":[{"kind":"event","id":"0191f2c5-1b2c-7d4e-8f90-a1b2c3d4e5f6","title":"Standup","snippet":"Agenda and notes","container":"Work","all_day":false,"done":false,"start":"2026-09-07T07:00:00Z","end":"2026-09-07T07:15:00Z"}],"next_offset":null}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/shares":{"get":{"tags":["Shares"],"operationId":"listShares","summary":"List share links","description":"Returns every share link of the account, newest first, including revoked ones so their access counts stay readable.\n\nRequires scope `shares:read`.\n","x-scope":"shares:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"200":{"description":"All share links of the account.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareList"},"example":{"items":[{"id":"0191f2f0-1111-7000-8000-a1b2c3d4e5f6","name":"Week · 7 – 13 September 2026","url":"https://calendar.akarpov.ru/s/2f8Qa1vX9c","view":"week","period":"2026-09-07","calendars":["0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10"],"detail":"titles","tz_mode":"owner","include_todos":false,"show_sleep":true,"active":true,"access_count":12,"last_accessed_at":"2026-09-12T08:14:02Z","created_at":"2026-09-07T09:00:00Z","updated_at":"2026-09-07T09:00:00Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"post":{"tags":["Shares"],"operationId":"createShare","summary":"Create a share link","description":"Creates a read-only link to one fixed period. The address is returned once here and stays readable in the list while the link is active.\n\nSend `Idempotency-Key` to make a retry safe.\n\nRequires scope `shares:write`.\n","x-scope":"shares:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareInput"},"example":{"name":"Week · 7 – 13 September 2026","view":"week","period":"2026-09-07","calendars":["0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10"],"detail":"titles"}}}},"responses":{"201":{"description":"The created share link.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Location":{"$ref":"#/components/headers/Location"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Share"},"example":{"id":"0191f2f0-1111-7000-8000-a1b2c3d4e5f6","name":"Week · 7 – 13 September 2026","url":"https://calendar.akarpov.ru/s/2f8Qa1vX9c","view":"week","period":"2026-09-07","calendars":["0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10"],"detail":"titles","tz_mode":"owner","include_todos":false,"show_sleep":true,"active":true,"access_count":12,"last_accessed_at":"2026-09-12T08:14:02Z","created_at":"2026-09-07T09:00:00Z","updated_at":"2026-09-07T09:00:00Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/shares/{id}":{"parameters":[{"$ref":"#/components/parameters/ShareID"}],"get":{"tags":["Shares"],"operationId":"getShare","summary":"Get a share link","description":"Returns one share link.\n\nRequires scope `shares:read`.\n","x-scope":"shares:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"200":{"description":"The share link.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Share"},"example":{"id":"0191f2f0-1111-7000-8000-a1b2c3d4e5f6","name":"Week · 7 – 13 September 2026","url":"https://calendar.akarpov.ru/s/2f8Qa1vX9c","view":"week","period":"2026-09-07","calendars":["0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10"],"detail":"titles","tz_mode":"owner","include_todos":false,"show_sleep":true,"active":true,"access_count":12,"last_accessed_at":"2026-09-12T08:14:02Z","created_at":"2026-09-07T09:00:00Z","updated_at":"2026-09-07T09:00:00Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"patch":{"tags":["Shares"],"operationId":"updateShare","summary":"Change a share link","description":"Changes the name, period, calendars or detail level. The address stays the same; use regenerate to replace it.\n\nRequires scope `shares:write`.\n","x-scope":"shares:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"parameters":[{"$ref":"#/components/parameters/IfMatch"}],"requestBody":{"required":true,"content":{"application/merge-patch+json":{"schema":{"$ref":"#/components/schemas/ShareInput"},"example":{"detail":"busy"}}}},"responses":{"200":{"description":"The updated share link.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Share"},"example":{"id":"0191f2f0-1111-7000-8000-a1b2c3d4e5f6","name":"Week · 7 – 13 September 2026","url":"https://calendar.akarpov.ru/s/2f8Qa1vX9c","view":"week","period":"2026-09-07","calendars":["0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10"],"detail":"titles","tz_mode":"owner","include_todos":false,"show_sleep":true,"active":true,"access_count":12,"last_accessed_at":"2026-09-12T08:14:02Z","created_at":"2026-09-07T09:00:00Z","updated_at":"2026-09-07T09:00:00Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"delete":{"tags":["Shares"],"operationId":"revokeShare","summary":"Revoke a share link","description":"Stops the address working. The row is kept so the access count stays readable, and the link can not be brought back; create a new one instead.\n\nRequires scope `shares:write`.\n","x-scope":"shares:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"responses":{"204":{"description":"The link was revoked.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/shares/{id}/regenerate":{"parameters":[{"$ref":"#/components/parameters/ShareID"}],"post":{"tags":["Shares"],"operationId":"regenerateShare","summary":"Replace the address of a share link","description":"Issues a new address and stops the old one working, for when a link was sent to the wrong person.\n\nRequires scope `shares:write`.\n","x-scope":"shares:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"responses":{"200":{"description":"The share link with its new address.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Share"},"example":{"id":"0191f2f0-1111-7000-8000-a1b2c3d4e5f6","name":"Week · 7 – 13 September 2026","url":"https://calendar.akarpov.ru/s/2f8Qa1vX9c","view":"week","period":"2026-09-07","calendars":["0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10"],"detail":"titles","tz_mode":"owner","include_todos":false,"show_sleep":true,"active":true,"access_count":12,"last_accessed_at":"2026-09-12T08:14:02Z","created_at":"2026-09-07T09:00:00Z","updated_at":"2026-09-07T09:00:00Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/changes":{"get":{"tags":["Changes"],"operationId":"listChanges","summary":"List changes since a sequence number","description":"Returns what changed in the account, in order, so a client can catch up without refetching everything. Poll with the `latest` value from the previous reply as `since`.\n\nEntries are kept for the retention window, so a client that has been away longer than that should refetch rather than replay.\n\nRequires scope `calendars:read`.\n","x-scope":"calendars:read","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"$ref":"#/components/parameters/ChangesSince"},{"$ref":"#/components/parameters/ChangesLimit"}],"responses":{"200":{"description":"Changes in ascending order, oldest first.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeList"},"example":{"items":[{"seq":42,"entity":"event","id":"0191f2c5-1b2c-7d4e-8f90-a1b2c3d4e5f6","op":"update","calendar_id":"0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","list_id":null,"from":"2026-09-07T07:00:00Z","to":"2026-09-07T07:15:00Z","origin":"google"}],"latest":57}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/sync/status":{"get":{"tags":["Sync"],"operationId":"getSyncStatus","summary":"Get Google sync status","description":"Returns whether Google is connected and what each binding is doing, including how many local changes are still waiting to be pushed.\n\nRequires scope `sync:write`, because the same scope covers starting a sync.\n","x-scope":"sync:write","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"200":{"description":"The sync status of the account.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncStatus"},"example":{"connected":true,"email":"sasha@gmail.com","status":"ok","bindings":[{"id":"0191f2f1-2222-7000-8000-a1b2c3d4e5f6","entity":"event","remote_id":"primary","remote_name":"sasha@gmail.com","local_id":"0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","direction":"both","enabled":true,"pending":0,"watching":true,"last_synced_at":"2026-09-12T08:10:00Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/sync/run":{"post":{"tags":["Sync"],"operationId":"runSync","summary":"Sync with Google now","description":"Queues a pull for every enabled binding. The work happens in the background, so this returns before it finishes; poll `/sync/status` to see it land.\n\nRequires scope `sync:write`.\n","x-scope":"sync:write","security":[{"bearerAuth":[]},{"sessionCookie":[],"csrfToken":[]}],"responses":{"202":{"description":"The sync was queued.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"cal_\u003cprefix\u003e_\u003csecret\u003e","description":"API token created in Settings → API tokens."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"__Host-sid","description":"Browser session cookie, set by signing in. It is named `sid` when the server runs over plain HTTP. Unsafe methods also need the `X-CSRF-Token` header."},"csrfToken":{"type":"apiKey","in":"header","name":"X-CSRF-Token","description":"CSRF token of the browser session, required together with `sessionCookie` on unsafe methods. Pages expose it in the `csrf-token` meta tag."}},"parameters":{"CalendarID":{"name":"id","in":"path","required":true,"description":"Calendar ID.","schema":{"type":"string","format":"uuid"},"example":"0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10"},"IfMatch":{"name":"If-Match","in":"header","required":false,"description":"ETag from an earlier response. The request fails with `412` if the resource changed since then.","schema":{"type":"string"},"example":"\"lx3kq9zb\""},"IfNoneMatch":{"name":"If-None-Match","in":"header","required":false,"description":"ETag from an earlier response. The server answers `304` without a body if the resource is unchanged.","schema":{"type":"string"},"example":"\"lx3kq9zb\""},"EventID":{"name":"id","in":"path","required":true,"description":"Event ID.","schema":{"type":"string","format":"uuid"},"example":"0191f2c5-1b2c-7d4e-8f90-a1b2c3d4e5f6"},"RangeFrom":{"name":"from","in":"query","required":true,"description":"Start of the range, inclusive. An RFC 3339 date-time, or a date for midnight in the account timezone.","schema":{"type":"string"},"example":"2026-09-07T00:00:00+03:00"},"RangeTo":{"name":"to","in":"query","required":true,"description":"End of the range, exclusive. An RFC 3339 date-time, or a date for midnight in the account timezone.","schema":{"type":"string"},"example":"2026-09-14"},"CalendarFilter":{"name":"calendar_id","in":"query","required":false,"description":"Only include these calendars. Repeat the parameter or separate IDs with commas. Without it, every calendar that is not hidden is included.","style":"form","explode":true,"schema":{"type":"array","items":{"type":"string","format":"uuid"}},"example":["0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10"]},"Expand":{"name":"expand","in":"query","required":false,"description":"Expand recurring series into occurrences.","schema":{"type":"boolean","default":true},"example":true},"EditScope":{"name":"scope","in":"query","required":false,"description":"Part of a recurring series to change: `this` occurrence, `following` occurrences, or `all`. Defaults to `this` when `instance` is set or the event is a changed occurrence, and to `all` otherwise.","schema":{"type":"string","enum":["this","following","all"]},"example":"this"},"Instance":{"name":"instance","in":"query","required":false,"description":"Original start of the occurrence, as returned in the `instance` field. Required with `this` and `following` when the path points at a series.","schema":{"type":"string"},"example":"2026-09-11T10:00:00+03:00"},"TodoListID":{"name":"id","in":"path","required":true,"description":"Todo list ID.","schema":{"type":"string","format":"uuid"},"example":"0191f2c7-3e4f-7a51-9b62-c3d4e5f60718"},"TodoID":{"name":"id","in":"path","required":true,"description":"Todo ID.","schema":{"type":"string","format":"uuid"},"example":"0191f2c8-4f50-7b62-8c73-d4e5f6071829"},"CheckID":{"name":"cid","in":"path","required":true,"description":"Checklist item ID.","schema":{"type":"string","format":"uuid"},"example":"0191f2c9-5061-7c73-9d84-e5f607182930"},"TodoListFilter":{"name":"list_id","in":"query","required":false,"description":"Only include these lists. Repeat the parameter or separate IDs with commas.","style":"form","explode":true,"schema":{"type":"array","items":{"type":"string","format":"uuid"}},"example":["0191f2c7-3e4f-7a51-9b62-c3d4e5f60718"]},"ParentFilter":{"name":"parent_id","in":"query","required":false,"description":"Only include subtasks of this todo.","schema":{"type":"string","format":"uuid"},"example":"0191f2c8-4f50-7b62-8c73-d4e5f6071829"},"TodoStatus":{"name":"status","in":"query","required":false,"description":"Which todos to include.","schema":{"type":"string","enum":["open","completed","all"],"default":"open"},"example":"open"},"DueFrom":{"name":"due_from","in":"query","required":false,"description":"Only include todos due on or after this date.","schema":{"type":"string","format":"date"},"example":"2026-09-10"},"DueTo":{"name":"due_to","in":"query","required":false,"description":"Only include todos due before this date.","schema":{"type":"string","format":"date"},"example":"2026-09-17"},"Cursor":{"name":"cursor","in":"query","required":false,"description":"The `next_cursor` of the previous page.","schema":{"type":"string"},"example":"0191f2c8-4f50-7b62-8c73-d4e5f6071829"},"Limit":{"name":"limit","in":"query","required":false,"description":"Items per page.","schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"example":100},"ImportID":{"name":"id","in":"path","required":true,"description":"Import ID.","schema":{"type":"string","format":"uuid"},"example":"0191f2d0-1111-7000-8000-a1b2c3d4e5f6"},"ImportLimit":{"name":"limit","in":"query","required":false,"description":"Imports to return, 1 to 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"example":20},"ExportCalendars":{"name":"calendar_id","in":"query","required":false,"description":"Only these calendars. Repeat the parameter or separate IDs with commas. Without it every calendar is included.","style":"form","explode":true,"schema":{"type":"array","items":{"type":"string","format":"uuid"}},"example":["0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10"]},"SubscriptionID":{"name":"id","in":"path","required":true,"description":"Subscription ID.","schema":{"type":"string","format":"uuid"},"example":"0191f2d1-2222-7000-8000-a1b2c3d4e5f6"},"DuplicateID":{"name":"id","in":"path","required":true,"description":"Duplicate pair ID.","schema":{"type":"string","format":"uuid"},"example":"0191f2e0-1111-7000-8000-a1b2c3d4e5f6"},"DuplicateStatus":{"name":"status","in":"query","required":false,"description":"Which pairs to return.","schema":{"type":"string","enum":["pending","merged","deleted","dismissed"],"default":"pending"},"example":"pending"},"TrashEntity":{"name":"entity","in":"path","required":true,"description":"Whether the entry is an event or a todo.","schema":{"type":"string","enum":["event","todo"]},"example":"event"},"TrashID":{"name":"id","in":"path","required":true,"description":"ID of the deleted entry.","schema":{"type":"string","format":"uuid"},"example":"0191f2c5-1b2c-7d4e-8f90-a1b2c3d4e5f6"},"TrashLimit":{"name":"limit","in":"query","required":false,"description":"Entries to return per kind, 1 to 500.","schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"example":100},"SearchQuery":{"name":"q","in":"query","required":true,"description":"The query, with words, phrases and filters.","schema":{"type":"string","maxLength":500},"example":"standup in:Work after:today"},"SearchType":{"name":"type","in":"query","required":false,"description":"Limit to one kind. A `type:` filter inside the query wins.","schema":{"type":"string","enum":["event","todo"]},"example":"todo"},"SearchLimit":{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"example":25},"SearchOffset":{"name":"offset","in":"query","required":false,"description":"Results to skip. Use `next_offset` from the previous page.","schema":{"type":"integer","minimum":0,"default":0},"example":0},"ShareID":{"name":"id","in":"path","required":true,"description":"Share link ID.","schema":{"type":"string","format":"uuid"},"example":"0191f2f0-1111-7000-8000-a1b2c3d4e5f6"},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Repeat this value to retry safely; the first reply is replayed instead of creating a second entry.","schema":{"type":"string","minLength":1,"maxLength":255},"example":"7b1f0c2e-9a44-4f7e-9f2e-3d5c8a1b2c3d"},"ChangesSince":{"name":"since","in":"query","required":false,"description":"Return changes after this sequence number. Use `latest` from the previous reply.","schema":{"type":"integer","format":"int64","minimum":0,"default":0},"example":42},"ChangesLimit":{"name":"limit","in":"query","required":false,"description":"Changes to return, 1 to 1000.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":200},"example":200}},"headers":{"ETag":{"description":"Version of the resource. Send it in `If-Match` to avoid overwriting other changes.","schema":{"type":"string"},"example":"\"lx3kq9zb\""},"Location":{"description":"URL of the created resource.","schema":{"type":"string"},"example":"/api/v1/calendars/0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10"},"RateLimitLimit":{"description":"Requests allowed per window.","schema":{"type":"integer","minimum":1},"example":600},"RateLimitRemaining":{"description":"Requests left in the current window.","schema":{"type":"integer","minimum":0},"example":599},"RateLimitReset":{"description":"Seconds until the full quota is available again.","schema":{"type":"integer","minimum":0},"example":1},"RetryAfter":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1},"example":12},"RequestID":{"description":"Request identifier, also returned in error bodies as `request_id`.","schema":{"type":"string"},"example":"tQ6DNZRbQcyrEPPA"}},"responses":{"PayloadTooLarge":{"description":"The uploaded file is larger than the server accepts.","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"about:blank","title":"Request Entity Too Large","status":413,"instance":"/api/v1/imports","request_id":"tQ6DNZRbQcyrEPPA"}}}},"NotModified":{"description":"The resource matches the ETag in `If-None-Match`.","headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"Unauthorized":{"description":"The token or session is missing, invalid, revoked or expired.","headers":{"WWW-Authenticate":{"description":"Authentication scheme to use.","schema":{"type":"string"},"example":"Bearer realm=\"calendar\""},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"about:blank","title":"Unauthorized","status":401,"detail":"invalid api token","instance":"/api/v1/me","request_id":"tQ6DNZRbQcyrEPPA"}}}},"Forbidden":{"description":"The token lacks the required scope, or a session request failed the CSRF or origin check.","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"scope":{"summary":"Missing scope","value":{"type":"about:blank","title":"Forbidden","status":403,"detail":"token lacks scope calendars:write","instance":"/api/v1/calendars","request_id":"tQ6DNZRbQcyrEPPA"}},"csrf":{"summary":"Missing CSRF token","value":{"type":"about:blank","title":"Forbidden","status":403,"detail":"invalid csrf token","instance":"/api/v1/calendars","request_id":"tQ6DNZRbQcyrEPPA"}}}}}},"NotFound":{"description":"The resource does not exist or belongs to another account.","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"about:blank","title":"Not Found","status":404,"instance":"/api/v1/calendars/0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","request_id":"tQ6DNZRbQcyrEPPA"}}}},"Conflict":{"description":"The request conflicts with the current state, for example deleting the default calendar.","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"about:blank","title":"Conflict","status":409,"detail":"the default calendar cannot be deleted","instance":"/api/v1/calendars/0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","request_id":"tQ6DNZRbQcyrEPPA"}}}},"PreconditionFailed":{"description":"The resource changed since the ETag in `If-Match` was issued.","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"about:blank","title":"Precondition Failed","status":412,"detail":"resource was modified, reload it and retry","instance":"/api/v1/calendars/0191f2c4-7a1b-7c3e-9a51-2b7d0e4f6a10","request_id":"tQ6DNZRbQcyrEPPA"}}}},"UnsupportedMediaType":{"description":"The request body has the wrong `Content-Type`.","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"about:blank","title":"Unsupported Media Type","status":415,"detail":"content type must be application/json","instance":"/api/v1/calendars","request_id":"tQ6DNZRbQcyrEPPA"}}}},"UnprocessableEntity":{"description":"The request body is well formed but some fields are invalid.","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"about:blank","title":"Unprocessable Entity","status":422,"detail":"One or more fields are invalid.","instance":"/api/v1/calendars","request_id":"tQ6DNZRbQcyrEPPA","errors":[{"field":"color","message":"must be a hex color like #3b6ea5"}]}}}},"TooManyRequests":{"description":"The rate limit was exceeded.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-ID":{"$ref":"#/components/headers/RequestID"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"about:blank","title":"Too Many Requests","status":429,"detail":"too many attempts, retry in 12s","instance":"/api/v1/me","request_id":"tQ6DNZRbQcyrEPPA"}}}}},"schemas":{"Share":{"type":"object","description":"A read-only link to one fixed period of the calendar.","additionalProperties":false,"required":["id","name","url","view","period","calendars","detail","tz_mode","include_todos","show_sleep","active","access_count","last_accessed_at","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"Share link ID."},"name":{"type":"string","maxLength":100,"description":"Name shown to whoever opens the link."},"url":{"type":"string","description":"Full address of the link. Empty once the link is revoked."},"view":{"type":"string","enum":["day","3day","week","month","year","agenda"],"description":"Layout the period is shown in."},"period":{"type":"string","format":"date","description":"First day of the period, as `YYYY-MM-DD`."},"calendars":{"type":"array","description":"Calendars included in the link. Empty means every calendar.","items":{"type":"string","format":"uuid"}},"detail":{"type":"string","enum":["busy","titles","full"],"description":"`busy` hides titles, `titles` shows them, `full` also shows descriptions and locations."},"tz_mode":{"type":"string","enum":["owner","viewer"],"description":"Whether times are shown in the owner's timezone or the viewer's."},"include_todos":{"type":"boolean","description":"Whether scheduled todos appear alongside events."},"show_sleep":{"type":"boolean","description":"Whether the sleep band is drawn."},"active":{"type":"boolean","description":"Whether the address still works."},"access_count":{"type":"integer","format":"int64","minimum":0,"description":"How many times the link has been opened."},"last_accessed_at":{"type":"string","format":"date-time","nullable":true,"description":"When the link was last opened."},"created_at":{"type":"string","format":"date-time","description":"When the link was created."},"updated_at":{"type":"string","format":"date-time","description":"When the link was last changed."}}},"ShareInput":{"type":"object","description":"Fields of a share link. Every field is optional on a change.","additionalProperties":false,"properties":{"name":{"type":"string","maxLength":100,"description":"Name shown to whoever opens the link."},"view":{"type":"string","enum":["day","3day","week","month","year","agenda"],"description":"Layout the period is shown in."},"period":{"type":"string","format":"date","description":"Any day inside the period, as `YYYY-MM-DD`; it is snapped to the start."},"calendars":{"type":"array","description":"Calendars to include. Empty means every calendar.","items":{"type":"string","format":"uuid"}},"detail":{"type":"string","enum":["busy","titles","full"],"description":"How much of each entry to show."},"tz_mode":{"type":"string","enum":["owner","viewer"],"description":"Whose timezone times are shown in."},"include_todos":{"type":"boolean","description":"Whether to include scheduled todos."},"show_sleep":{"type":"boolean","description":"Whether to draw the sleep band."}}},"ShareList":{"type":"object","description":"Share links of the account.","additionalProperties":false,"required":["items"],"properties":{"items":{"type":"array","description":"The links, newest first.","items":{"$ref":"#/components/schemas/Share"}}}},"Change":{"type":"object","description":"One recorded change to an event or todo.","additionalProperties":false,"required":["seq","entity","id","op","calendar_id","list_id","from","to"],"properties":{"seq":{"type":"integer","format":"int64","description":"Position in the account's change log. Always increasing."},"entity":{"type":"string","enum":["event","todo","calendar","list","settings","share"],"description":"What changed."},"id":{"type":"string","format":"uuid","description":"ID of the entry that changed."},"op":{"type":"string","enum":["create","update","delete"],"description":"What happened to it."},"calendar_id":{"type":"string","format":"uuid","nullable":true,"description":"Calendar the entry belongs to, for events."},"list_id":{"type":"string","format":"uuid","nullable":true,"description":"List the entry belongs to, for todos."},"from":{"type":"string","format":"date-time","nullable":true,"description":"Start of the range the change affects, for refreshing a view."},"to":{"type":"string","format":"date-time","nullable":true,"description":"End of that range."},"origin":{"type":"string","description":"What made the change, such as `google` or `import`. Empty when it came from this app."}}},"ChangeList":{"type":"object","description":"Changes in ascending order, oldest first.","additionalProperties":false,"required":["items","latest"],"properties":{"items":{"type":"array","description":"The changes.","items":{"$ref":"#/components/schemas/Change"}},"latest":{"type":"integer","format":"int64","description":"Newest sequence number in the account. Send it back as `since` to continue."}}},"SyncStatus":{"type":"object","description":"Whether Google is connected and what each binding is doing.","additionalProperties":false,"required":["connected","email","status","bindings"],"properties":{"connected":{"type":"boolean","description":"Whether a usable Google connection exists."},"email":{"type":"string","description":"Google account the connection belongs to."},"status":{"type":"string","description":"`ok`, or why the connection stopped working."},"last_error":{"type":"string","description":"Detail of the last failure."},"bindings":{"type":"array","description":"One per calendar or list being synced.","items":{"$ref":"#/components/schemas/SyncBinding"}}}},"SyncBinding":{"type":"object","description":"A local calendar or list paired with a Google one.","additionalProperties":false,"required":["id","entity","remote_id","remote_name","local_id","direction","enabled","pending","watching","last_synced_at"],"properties":{"id":{"type":"string","format":"uuid","description":"Binding ID."},"entity":{"type":"string","enum":["event","todo"],"description":"Whether the pair holds events or todos."},"remote_id":{"type":"string","description":"Google's identifier for the calendar or task list."},"remote_name":{"type":"string","description":"Name of the Google calendar or task list."},"local_id":{"type":"string","format":"uuid","description":"Local calendar or list it is paired with."},"direction":{"type":"string","enum":["both","pull","push"],"description":"Which way changes travel."},"enabled":{"type":"boolean","description":"Whether this pair is syncing."},"pending":{"type":"integer","minimum":0,"description":"Local changes still waiting to reach Google."},"watching":{"type":"boolean","description":"Whether Google pushes notifications for this pair, rather than being polled."},"last_synced_at":{"type":"string","format":"date-time","nullable":true,"description":"When this pair last synced."}}},"ICSDocument":{"type":"string","description":"An iCalendar document."},"CSVDocument":{"type":"string","description":"A CSV file with one row per event."},"AccountYAML":{"type":"string","description":"The whole account in this app's own YAML format."},"Import":{"type":"object","description":"An uploaded file, what it holds, and the result of writing it in.","additionalProperties":false,"required":["id","source","filename","status","preview","stats","created_at","expires_at","finished_at"],"properties":{"id":{"type":"string","format":"uuid","description":"Import ID."},"source":{"type":"string","enum":["ics","csv","yaml","ics_url"],"description":"File type detected from the content and name."},"filename":{"type":"string","maxLength":255,"description":"Name of the uploaded file."},"status":{"type":"string","enum":["pending","previewed","committing","done","failed","expired"],"description":"`previewed` is waiting to be committed, `done` finished, `failed` could not be written in."},"preview":{"allOf":[{"$ref":"#/components/schemas/ImportPreview"}],"nullable":true,"description":"What the file holds. Null when listing."},"stats":{"allOf":[{"$ref":"#/components/schemas/ImportStats"}],"nullable":true,"description":"Result of writing the file in. Null before it is committed."},"error":{"type":"string","description":"Why the import failed."},"created_at":{"type":"string","format":"date-time","description":"When the file was uploaded."},"expires_at":{"type":"string","format":"date-time","description":"When the uploaded file is discarded and can no longer be committed."},"finished_at":{"type":"string","format":"date-time","nullable":true,"description":"When the import was written in."}}},"ImportList":{"type":"object","description":"Recent imports, newest first.","additionalProperties":false,"required":["items"],"properties":{"items":{"type":"array","description":"Imports without their previews.","items":{"$ref":"#/components/schemas/Import"}}}},"ImportPreview":{"type":"object","description":"What an uploaded file holds, before anything is written.","additionalProperties":false,"required":["source","filename","timezone","targets","stats","samples","problems","settings"],"properties":{"source":{"type":"string","description":"File type the preview was read with."},"filename":{"type":"string","description":"Name of the uploaded file."},"timezone":{"type":"string","description":"IANA timezone used to read times without an offset."},"targets":{"type":"array","description":"One per calendar or list found in the file.","items":{"$ref":"#/components/schemas/ImportTarget"}},"stats":{"$ref":"#/components/schemas/ImportStats"},"samples":{"type":"array","maxItems":50,"description":"A few entries with what would happen to each.","items":{"$ref":"#/components/schemas/ImportSample"}},"problems":{"type":"array","description":"Entries that could not be read.","items":{"$ref":"#/components/schemas/ImportProblem"}},"settings":{"type":"boolean","description":"Whether the file also carries account settings."}}},"ImportTarget":{"type":"object","description":"A calendar or list found in the file, and where it would be imported.","additionalProperties":false,"required":["name","kind","target","events","todos","skip","index"],"properties":{"name":{"type":"string","description":"Name in the file."},"kind":{"type":"string","enum":["calendar","tasklist"],"description":"Whether it holds events or todos."},"target":{"type":"string","description":"Calendar or list ID to import into, or `new` to create one."},"events":{"type":"integer","minimum":0,"description":"Events found, counting changed occurrences."},"todos":{"type":"integer","minimum":0,"description":"Todos found, counting subtasks."},"skip":{"type":"boolean","description":"Whether this target is left out."},"index":{"type":"integer","minimum":0,"description":"Send this back in the commit body to choose where it goes."},"timezone":{"type":"string","description":"Timezone declared in the file for this calendar."}}},"ImportStats":{"type":"object","description":"How many entries fall into each outcome.","additionalProperties":false,"required":["new","updated","duplicate","invalid","skipped"],"properties":{"new":{"type":"integer","minimum":0,"description":"Entries that would be added."},"updated":{"type":"integer","minimum":0,"description":"Entries matching an existing one by UID."},"duplicate":{"type":"integer","minimum":0,"description":"Entries with the same title, time and place as one already here."},"invalid":{"type":"integer","minimum":0,"description":"Entries that could not be read."},"skipped":{"type":"integer","minimum":0,"description":"Entries in targets left out."}}},"ImportSample":{"type":"object","description":"One entry from the file with what would happen to it.","additionalProperties":false,"required":["where","title","when","action"],"properties":{"where":{"type":"string","description":"Where it sits in the file."},"title":{"type":"string","description":"Title of the entry."},"when":{"type":"string","description":"Date, or date and time, of the entry."},"action":{"type":"string","enum":["new","updated","duplicate"],"description":"What would happen on commit."}}},"ImportProblem":{"type":"object","description":"An entry in the file that could not be read.","additionalProperties":false,"required":["where","message"],"properties":{"where":{"type":"string","description":"Where it sits in the file."},"message":{"type":"string","description":"What is wrong with it."}}},"ImportCommit":{"type":"object","description":"Where each part of the file goes.","additionalProperties":false,"properties":{"targets":{"type":"array","description":"Choices by target index. Targets left out keep the choice from the preview.","items":{"$ref":"#/components/schemas/ImportChoice"}}}},"ImportChoice":{"type":"object","description":"Where one target goes.","additionalProperties":false,"required":["index"],"properties":{"index":{"type":"integer","minimum":0,"description":"The `index` of a target from the preview."},"target":{"type":"string","description":"Calendar or list ID to import into, or `new` to create one."},"skip":{"type":"boolean","description":"Leave this target out."}}},"Subscription":{"type":"object","description":"An ICS feed followed as a read-only calendar.","additionalProperties":false,"required":["id","calendar_id","url","refresh_every","status","next_refresh_at","last_ok_at"],"properties":{"id":{"type":"string","format":"uuid","description":"Subscription ID."},"calendar_id":{"type":"string","format":"uuid","description":"Read-only calendar filled from the feed."},"url":{"type":"string","description":"Address of the feed."},"refresh_every":{"type":"string","description":"How often the feed is fetched, as a duration like `1h0m0s`."},"status":{"type":"string","enum":["ok","error","paused"],"description":"`error` means it has failed several times in a row; the calendar keeps its last content."},"next_refresh_at":{"type":"string","format":"date-time","description":"When the next fetch is due."},"last_ok_at":{"type":"string","format":"date-time","nullable":true,"description":"When the feed was last fetched successfully."},"last_error":{"type":"string","description":"Why the last fetch failed."}}},"SubscriptionInput":{"type":"object","description":"A feed to follow.","additionalProperties":false,"required":["url"],"properties":{"url":{"type":"string","description":"An http, https or webcal link to an .ics file."},"name":{"type":"string","maxLength":100,"description":"Name for the calendar. Defaults to the host."},"refresh_every":{"type":"string","description":"How often to fetch, as a duration like `1h`. Clamped to the server minimum."}}},"SubscriptionList":{"type":"object","description":"All subscriptions of the account.","additionalProperties":false,"required":["items"],"properties":{"items":{"type":"array","description":"Subscriptions in the order they were created.","items":{"$ref":"#/components/schemas/Subscription"}}}},"Duplicate":{"type":"object","description":"Two entries that look like copies of each other.","additionalProperties":false,"required":["id","entity","reason","score","status","a","b","created_at","resolved_at"],"properties":{"id":{"type":"string","format":"uuid","description":"Pair ID."},"entity":{"type":"string","enum":["event","todo"],"description":"What the pair holds."},"reason":{"type":"string","enum":["uid","fingerprint","fuzzy"],"description":"`uid` share an identifier, `fingerprint` match on title, time and place, `fuzzy` have similar titles at a similar time."},"score":{"type":"number","minimum":0,"maximum":1,"description":"How close the two are, 1 for an exact match."},"status":{"type":"string","enum":["pending","merged","deleted","dismissed"],"description":"`pending` is waiting for review."},"a":{"$ref":"#/components/schemas/DuplicateSide"},"b":{"$ref":"#/components/schemas/DuplicateSide"},"created_at":{"type":"string","format":"date-time","description":"When the pair was found."},"resolved_at":{"type":"string","format":"date-time","nullable":true,"description":"When the pair was resolved."}}},"DuplicateSide":{"type":"object","description":"One entry of a pair, as it looked when the pair was found.","additionalProperties":false,"required":["id","title","when","where","created"],"properties":{"id":{"type":"string","format":"uuid","description":"ID of the entry."},"title":{"type":"string","description":"Title of the entry."},"when":{"type":"string","description":"Date, or date and time, of the entry."},"where":{"type":"string","description":"Calendar or list it belongs to."},"body":{"type":"string","description":"Description or notes."},"location":{"type":"string","description":"Location of an event."},"extra":{"type":"array","description":"Short notes such as whether it repeats or how many reminders it has.","items":{"type":"string"}},"created":{"type":"string","format":"date-time","description":"When the entry was created."}}},"DuplicateList":{"type":"object","description":"Duplicate pairs, newest first.","additionalProperties":false,"required":["items"],"properties":{"items":{"type":"array","description":"The pairs.","items":{"$ref":"#/components/schemas/Duplicate"}}}},"DuplicateResolve":{"type":"object","description":"What to do with a pair.","additionalProperties":false,"required":["action"],"properties":{"action":{"type":"string","enum":["merge","keep_a","keep_b","dismiss"],"description":"`merge` combines them, `keep_a` and `keep_b` trash the other, `dismiss` keeps both."}}},"TrashItem":{"type":"object","description":"A deleted event or todo.","additionalProperties":false,"required":["id","entity","title","when","where","children","deleted_at"],"properties":{"id":{"type":"string","format":"uuid","description":"ID of the entry."},"entity":{"type":"string","enum":["event","todo"],"description":"What the entry is."},"title":{"type":"string","description":"Title of the entry."},"when":{"type":"string","description":"Date, or date and time, of the entry."},"where":{"type":"string","description":"Calendar or list it belonged to."},"children":{"type":"integer","minimum":0,"description":"Changed occurrences or subtasks deleted with it."},"deleted_at":{"type":"string","format":"date-time","description":"When it was deleted."}}},"TrashList":{"type":"object","description":"Deleted entries, newest first.","additionalProperties":false,"required":["items"],"properties":{"items":{"type":"array","description":"The entries.","items":{"$ref":"#/components/schemas/TrashItem"}}}},"SearchResult":{"type":"object","description":"Matching entries, best first.","additionalProperties":false,"required":["query","type","items","next_offset"],"properties":{"query":{"type":"string","description":"The query as it was read."},"type":{"type":"string","description":"Kind the search was limited to, empty for both."},"items":{"type":"array","description":"The matches.","items":{"$ref":"#/components/schemas/SearchHit"}},"next_offset":{"type":"integer","nullable":true,"description":"Offset of the next page, null on the last page."},"warnings":{"type":"array","description":"Parts of the query that were not understood.","items":{"type":"string"}}}},"SearchHit":{"type":"object","description":"One matching event or todo.","additionalProperties":false,"required":["kind","id","title","snippet","container","all_day","done","start","end"],"properties":{"kind":{"type":"string","enum":["event","todo"],"description":"What the entry is."},"id":{"type":"string","format":"uuid","description":"ID of the entry."},"title":{"type":"string","description":"Title of the entry."},"snippet":{"type":"string","description":"Start of the description or notes, with Markdown stripped."},"container":{"type":"string","description":"Calendar or list it belongs to."},"all_day":{"type":"boolean","description":"Whether the event spans whole days, or the todo is due on a date without a time."},"done":{"type":"boolean","description":"Whether a todo is completed. Always false for events."},"start":{"type":"string","format":"date-time","nullable":true,"description":"Start of an event, or the due date of a todo. Null for a todo without a date."},"end":{"type":"string","format":"date-time","nullable":true,"description":"End of an event. Null for todos."}}},"User":{"type":"object","description":"A user account with its display settings.","additionalProperties":false,"required":["id","email","display_name","timezone","email_verified","week_start","time_format","default_view","date_only_reminder","sleep","created_at"],"properties":{"id":{"type":"string","format":"uuid","description":"Account ID."},"email":{"type":"string","format":"email","description":"Email address used to sign in."},"display_name":{"type":"string","maxLength":100,"description":"Name shown in the interface. Empty when not set."},"timezone":{"type":"string","description":"IANA timezone used to show times, for example `Europe/Moscow`."},"email_verified":{"type":"boolean","description":"Whether the email address has been confirmed."},"week_start":{"type":"integer","minimum":0,"maximum":6,"description":"First day of the week, 0 for Sunday through 6 for Saturday."},"time_format":{"type":"string","enum":["12h","24h"],"description":"Clock used to show times."},"default_view":{"type":"string","enum":["day","3day","week","month","year","agenda"],"description":"View opened on the start page."},"date_only_reminder":{"type":"string","description":"Time of day a reminder fires for a todo that has a date but no time, as `HH:MM`."},"sleep":{"$ref":"#/components/schemas/Sleep"},"created_at":{"type":"string","format":"date-time","description":"When the account was created."}}},"Sleep":{"type":"object","description":"Sleep hours shaded on day, 3-day and week views. Events can still be placed inside them.","additionalProperties":false,"required":["enabled","windows"],"properties":{"enabled":{"type":"boolean","description":"Whether sleep hours are shown."},"windows":{"type":"array","maxItems":7,"description":"At most one window per weekday. A window ending earlier than it starts crosses midnight.","items":{"$ref":"#/components/schemas/SleepWindow"}}}},"SleepWindow":{"type":"object","description":"Sleep hours starting on one weekday, in the account timezone.","additionalProperties":false,"required":["weekday","start","end"],"properties":{"weekday":{"type":"integer","minimum":0,"maximum":6,"description":"Weekday the window starts on, 0 for Sunday."},"start":{"type":"string","pattern":"^[0-2][0-9]:[0-5][0-9]$","description":"Local time sleep starts, like 23:30."},"end":{"type":"string","pattern":"^[0-2][0-9]:[0-5][0-9]$","description":"Local time sleep ends, like 07:30. Earlier than start means the next day."}}},"AccountInput":{"type":"object","description":"Account settings to change. Send only the fields to change.","additionalProperties":false,"properties":{"display_name":{"type":"string","maxLength":100,"description":"Name shown in the interface."},"timezone":{"type":"string","description":"IANA timezone, for example `Europe/Vilnius`."},"week_start":{"type":"integer","minimum":0,"maximum":6,"description":"First day of the week, 0 for Sunday."},"time_format":{"type":"string","enum":["12h","24h"],"description":"Clock used to show times."},"default_view":{"type":"string","enum":["day","3day","week","month","year","agenda"],"description":"View opened on the start page."},"sleep":{"$ref":"#/components/schemas/Sleep"}}},"Calendar":{"type":"object","description":"A calendar that groups events.","additionalProperties":false,"required":["id","name","color","description","timezone","kind","read_only","is_default","hidden","position","default_reminders","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"Calendar ID."},"name":{"type":"string","minLength":1,"maxLength":100,"description":"Name shown in the interface."},"color":{"type":"string","pattern":"^#[0-9a-f]{6}$","description":"Color of the calendar's events as a lowercase hex value."},"description":{"type":"string","maxLength":2000,"description":"Free text description. Empty when not set."},"timezone":{"type":"string","nullable":true,"description":"IANA timezone for new events, or null to use the account timezone."},"kind":{"type":"string","enum":["local","subscription"],"description":"`local` for calendars kept in this app, `subscription` for calendars filled from an ICS feed."},"read_only":{"type":"boolean","description":"Whether events of this calendar can be changed. Subscription calendars are read only."},"is_default":{"type":"boolean","description":"Whether new events go here when no calendar is chosen. The default calendar cannot be deleted."},"hidden":{"type":"boolean","description":"Whether the calendar is hidden from calendar views."},"position":{"type":"integer","minimum":0,"description":"Sort order in lists, lowest first."},"default_reminders":{"type":"array","maxItems":5,"description":"Minutes before start at which new events of this calendar remind by default.","items":{"type":"integer","minimum":0,"maximum":40320}},"created_at":{"type":"string","format":"date-time","description":"When the calendar was created."},"updated_at":{"type":"string","format":"date-time","description":"When the calendar was last changed."}}},"CalendarList":{"type":"object","description":"All calendars of the account.","additionalProperties":false,"required":["items"],"properties":{"items":{"type":"array","description":"Calendars ordered by position.","items":{"$ref":"#/components/schemas/Calendar"}}}},"CalendarInput":{"type":"object","description":"Calendar fields to set. On create only `name` is required. On update send only the fields to change.","additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Name shown in the interface."},"color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$","description":"Hex color. Stored in lowercase."},"description":{"type":"string","maxLength":2000,"description":"Free text description."},"timezone":{"type":"string","nullable":true,"description":"IANA timezone for new events. Null or an empty string uses the account timezone."},"hidden":{"type":"boolean","description":"Hide the calendar from calendar views."},"position":{"type":"integer","minimum":0,"maximum":10000,"description":"Sort order in lists, lowest first."},"default_reminders":{"type":"array","maxItems":5,"description":"Minutes before start for default reminders. Duplicates are removed.","items":{"type":"integer","minimum":0,"maximum":40320}}}},"Problem":{"type":"object","description":"Error details in the RFC 9457 format.","additionalProperties":false,"required":["type","title","status"],"properties":{"type":{"type":"string","description":"URI that identifies the error type. `about:blank` means the HTTP status explains it."},"title":{"type":"string","description":"Short summary of the HTTP status."},"status":{"type":"integer","description":"HTTP status code."},"detail":{"type":"string","description":"Explanation specific to this request."},"instance":{"type":"string","description":"Path of the request that failed."},"request_id":{"type":"string","description":"Identifier of the request, the same as the `X-Request-ID` header."},"errors":{"type":"array","description":"Invalid fields, present on `422` responses.","items":{"$ref":"#/components/schemas/FieldError"}}}},"FieldError":{"type":"object","description":"One invalid input field.","additionalProperties":false,"required":["field","message"],"properties":{"field":{"type":"string","description":"Name of the field in the request."},"message":{"type":"string","description":"What is wrong with the value."}}},"Event":{"type":"object","description":"An event, a recurring series, or one occurrence of a series.","additionalProperties":false,"required":["id","calendar_id","uid","series_id","instance","recurring","title","body","location","url","all_day","start","end","timezone","rrule","rdate","exdate","status","transparency","visibility","color","read_only","reminders","version","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"Event ID. Occurrences of a series share the series ID."},"calendar_id":{"type":"string","format":"uuid","description":"Calendar the event belongs to."},"uid":{"type":"string","description":"iCalendar UID, shared by a series and its changed occurrences."},"series_id":{"type":"string","format":"uuid","nullable":true,"description":"Series of a changed occurrence, null otherwise."},"instance":{"type":"string","nullable":true,"description":"Original start of the occurrence within its series, null for single events and unexpanded series."},"recurring":{"type":"boolean","description":"Whether the event is a series or belongs to one."},"title":{"type":"string","maxLength":1000,"description":"Title. Empty when not set."},"body":{"type":"string","maxLength":100000,"description":"Description in Markdown."},"location":{"type":"string","maxLength":1000,"description":"Location text."},"url":{"type":"string","maxLength":2048,"description":"Link for the event, an http or https URL. Empty when not set."},"all_day":{"type":"boolean","description":"Whether the event spans whole days."},"start":{"type":"string","description":"Start as an RFC 3339 date-time in the event timezone, or a date for all-day events."},"end":{"type":"string","description":"End in the same format as `start`. Exclusive for all-day events."},"timezone":{"type":"string","nullable":true,"description":"IANA timezone of a timed event, null for all-day events."},"rrule":{"type":"string","nullable":true,"description":"RFC 5545 recurrence rule without the `RRULE:` prefix, null for events that do not repeat."},"rdate":{"type":"array","description":"Extra occurrence starts.","items":{"type":"string"}},"exdate":{"type":"array","description":"Starts of removed occurrences.","items":{"type":"string"}},"status":{"type":"string","enum":["confirmed","tentative","cancelled"],"description":"Event status. Cancelled occurrences are hidden from ranges."},"transparency":{"type":"string","enum":["opaque","transparent"],"description":"`opaque` blocks time, `transparent` shows the event without blocking it."},"visibility":{"type":"string","enum":["default","private"],"description":"Private events never appear on share links."},"color":{"type":"string","nullable":true,"pattern":"^#[0-9a-f]{6}$","description":"Event color, null to use the calendar color."},"read_only":{"type":"boolean","description":"Whether the event can be changed."},"reminders":{"type":"array","maxItems":5,"description":"Minutes before start at which to remind.","items":{"type":"integer","minimum":0,"maximum":40320}},"version":{"type":"integer","minimum":1,"description":"Increases with every change."},"created_at":{"type":"string","format":"date-time","description":"When the event was created."},"updated_at":{"type":"string","format":"date-time","description":"When the event was last changed."}}},"EventList":{"type":"object","description":"Events ordered by start.","additionalProperties":false,"required":["items"],"properties":{"items":{"type":"array","description":"Events and occurrences.","items":{"$ref":"#/components/schemas/Event"}}}},"EventInput":{"type":"object","description":"Event fields to set. On create only `start` is required. On update send only the fields to change, and `null` to clear a field.","additionalProperties":false,"properties":{"calendar_id":{"type":"string","format":"uuid","description":"Calendar for the event. Defaults to the default calendar."},"uid":{"type":"string","maxLength":1000,"description":"iCalendar UID, only on create. Defaults to the event ID."},"title":{"type":"string","maxLength":1000,"description":"Title."},"body":{"type":"string","maxLength":100000,"description":"Description in Markdown."},"location":{"type":"string","nullable":true,"maxLength":1000,"description":"Location text."},"url":{"type":"string","nullable":true,"maxLength":2048,"description":"An http or https link."},"all_day":{"type":"boolean","description":"Whether the event spans whole days. Changing it requires `start`."},"start":{"type":"string","description":"RFC 3339 date-time, a local date-time like `2026-09-11T10:00` read in `timezone`, or a date for all-day events."},"end":{"type":"string","nullable":true,"description":"End in the same format as `start`. Null resets to the default duration."},"timezone":{"type":"string","nullable":true,"description":"IANA timezone of a timed event."},"rrule":{"type":"string","nullable":true,"maxLength":1000,"description":"RFC 5545 recurrence rule, daily or less often. Null stops repeating."},"rdate":{"type":"array","nullable":true,"maxItems":1000,"description":"Extra occurrence starts in the same format as `start`.","items":{"type":"string"}},"exdate":{"type":"array","nullable":true,"maxItems":5000,"description":"Starts of occurrences to remove, in the same format as `start`.","items":{"type":"string"}},"status":{"type":"string","enum":["confirmed","tentative","cancelled"],"description":"Event status."},"transparency":{"type":"string","enum":["opaque","transparent"],"description":"Whether the event blocks time."},"visibility":{"type":"string","enum":["default","private"],"description":"Private events never appear on share links."},"color":{"type":"string","nullable":true,"pattern":"^#[0-9a-fA-F]{6}$","description":"Event color, null to use the calendar color."},"reminders":{"type":"array","nullable":true,"maxItems":5,"description":"Minutes before start. Null or an empty list removes reminders.","items":{"type":"integer","minimum":0,"maximum":40320}}}},"TodoList":{"type":"object","description":"A list that groups todos.","additionalProperties":false,"required":["id","name","color","position","is_default","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"List ID."},"name":{"type":"string","minLength":1,"maxLength":100,"description":"Name shown in the interface."},"color":{"type":"string","pattern":"^#[0-9a-f]{6}$","description":"Color of the list as a lowercase hex value."},"position":{"type":"integer","minimum":0,"description":"Sort order, lowest first."},"is_default":{"type":"boolean","description":"Whether new todos go here when no list is chosen. The default list cannot be deleted."},"created_at":{"type":"string","format":"date-time","description":"When the list was created."},"updated_at":{"type":"string","format":"date-time","description":"When the list was last changed."}}},"TodoListList":{"type":"object","description":"All todo lists of the account.","additionalProperties":false,"required":["items"],"properties":{"items":{"type":"array","description":"Lists ordered by position.","items":{"$ref":"#/components/schemas/TodoList"}}}},"TodoListInput":{"type":"object","description":"List fields to set. On create only `name` is required.","additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Name shown in the interface."},"color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$","description":"Hex color. Stored in lowercase."},"position":{"type":"integer","minimum":0,"maximum":10000,"description":"Sort order, lowest first."}}},"Check":{"type":"object","description":"One checklist item.","additionalProperties":false,"required":["id","text","done","position","done_at"],"properties":{"id":{"type":"string","format":"uuid","description":"Item ID."},"text":{"type":"string","minLength":1,"maxLength":500,"description":"Item text."},"done":{"type":"boolean","description":"Whether the item is checked."},"position":{"type":"string","description":"Sort key, items sort by it as plain strings."},"done_at":{"type":"string","format":"date-time","nullable":true,"description":"When the item was checked, null when unchecked."}}},"CheckList":{"type":"object","description":"Checklist items in order.","additionalProperties":false,"required":["items"],"properties":{"items":{"type":"array","description":"Items ordered by position.","items":{"$ref":"#/components/schemas/Check"}}}},"CheckInput":{"type":"object","description":"Checklist item fields. `text` is required when adding an item.","additionalProperties":false,"properties":{"text":{"type":"string","minLength":1,"maxLength":500,"description":"Item text."},"done":{"type":"boolean","description":"Whether the item is checked."}}},"CheckOrder":{"type":"object","description":"New order of a checklist.","additionalProperties":false,"required":["ids"],"properties":{"ids":{"type":"array","description":"Every item ID of the todo in the new order.","items":{"type":"string","format":"uuid"}}}},"Todo":{"type":"object","description":"A todo with its checklist.","additionalProperties":false,"required":["id","list_id","parent_id","title","body","status","priority","position","due_date","due_time","duration_min","timezone","rrule","show_on_calendar","completed_at","reminders","checks","version","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"Todo ID."},"list_id":{"type":"string","format":"uuid","description":"List the todo belongs to."},"parent_id":{"type":"string","format":"uuid","nullable":true,"description":"Parent of a subtask, null for top-level todos."},"title":{"type":"string","minLength":1,"maxLength":1000,"description":"Title."},"body":{"type":"string","maxLength":100000,"description":"Notes in Markdown."},"status":{"type":"string","enum":["needs_action","completed"],"description":"Todo status."},"priority":{"type":"integer","minimum":0,"maximum":3,"description":"0 none, 1 low, 2 medium, 3 high."},"position":{"type":"string","description":"Sort key within the list and parent, todos sort by it as plain strings."},"due_date":{"type":"string","format":"date","nullable":true,"description":"Due date."},"due_time":{"type":"string","nullable":true,"pattern":"^[0-2][0-9]:[0-5][0-9]$","description":"Due time in `timezone`, null for todos due on a date."},"duration_min":{"type":"integer","nullable":true,"minimum":1,"maximum":1440,"description":"Length of the calendar block in minutes, null for the default of 30."},"timezone":{"type":"string","nullable":true,"description":"IANA timezone of `due_time`."},"rrule":{"type":"string","nullable":true,"description":"RFC 5545 recurrence rule. Completing the todo creates the next occurrence."},"show_on_calendar":{"type":"boolean","description":"Whether the todo appears in calendar views."},"completed_at":{"type":"string","format":"date-time","nullable":true,"description":"When the todo was completed."},"reminders":{"type":"array","maxItems":5,"description":"Minutes before the due time at which to remind.","items":{"type":"integer","minimum":0,"maximum":40320}},"checks":{"type":"array","description":"Checklist items in order.","items":{"$ref":"#/components/schemas/Check"}},"version":{"type":"integer","minimum":1,"description":"Increases with every change, including checklist changes."},"created_at":{"type":"string","format":"date-time","description":"When the todo was created."},"updated_at":{"type":"string","format":"date-time","description":"When the todo was last changed."}}},"TodoPage":{"type":"object","description":"One page of todos.","additionalProperties":false,"required":["items","next_cursor"],"properties":{"items":{"type":"array","description":"Todos in creation order.","items":{"$ref":"#/components/schemas/Todo"}},"next_cursor":{"type":"string","nullable":true,"description":"Cursor for the next page, null on the last page."}}},"TodoCompletion":{"type":"object","description":"Result of completing a todo.","additionalProperties":false,"required":["todo","next_id"],"properties":{"todo":{"$ref":"#/components/schemas/Todo"},"next_id":{"type":"string","format":"uuid","nullable":true,"description":"Next occurrence of a recurring todo, null otherwise."}}},"TodoMove":{"type":"object","description":"Where to move a todo.","additionalProperties":false,"properties":{"list_id":{"type":"string","format":"uuid","description":"Target list."},"parent_id":{"type":"string","format":"uuid","nullable":true,"description":"New parent, null to make the todo top-level."},"after_id":{"type":"string","format":"uuid","nullable":true,"description":"Place the todo right after this one, null to place it first."}}},"TodoInput":{"type":"object","description":"Todo fields to set. On create only `title` is required. On update send only the fields to change, and `null` to clear a field.","additionalProperties":false,"properties":{"list_id":{"type":"string","format":"uuid","description":"List for the todo."},"parent_id":{"type":"string","format":"uuid","nullable":true,"description":"Parent todo, which must be top-level. Subtasks always share the parent's list."},"title":{"type":"string","minLength":1,"maxLength":1000,"description":"Title."},"body":{"type":"string","maxLength":100000,"description":"Notes in Markdown."},"priority":{"type":"integer","minimum":0,"maximum":3,"description":"0 none, 1 low, 2 medium, 3 high."},"due_date":{"type":"string","nullable":true,"description":"Due date like `2026-09-12`."},"due_time":{"type":"string","nullable":true,"description":"Due time like `18:00`, requires `due_date`."},"duration_min":{"type":"integer","nullable":true,"minimum":1,"maximum":1440,"description":"Length of the calendar block in minutes."},"timezone":{"type":"string","nullable":true,"description":"IANA timezone of `due_time`."},"rrule":{"type":"string","nullable":true,"maxLength":1000,"description":"RFC 5545 recurrence rule, requires `due_date`."},"show_on_calendar":{"type":"boolean","description":"Whether the todo appears in calendar views."},"reminders":{"type":"array","nullable":true,"maxItems":5,"description":"Minutes before the due time.","items":{"type":"integer","minimum":0,"maximum":40320}},"checks":{"type":"array","maxItems":200,"description":"Initial checklist, only on create.","items":{"$ref":"#/components/schemas/CheckInput"}}}}}}}