REST API reference
The SwitchDesk AI Access REST API: one route per tool at api.switchdesk.org/v1, auth header, GET vs POST, responses, errors, idempotency and limits.
Base URL and auth
https://api.switchdesk.org/v1/<tool>
Authorization: Bearer sda_…The key (sda_ + 32 characters + an 8-character checksum) is created in Settings → AI Access and shown once. Send it only in the Authorization header, never in a URL. Sign-in tokens don’t work here: they belong to https://mcp.switchdesk.org/mcp. The full schema is openapi.json.
Requests
- Every tool is
POST /v1/<tool>with a JSON body of its arguments (see the tool reference). - Reads without user-written text also accept
GETwith query parameters. Anything carrying text (query, titles, notes) must be a POST body, because URLs end up in logs. - Browsers can’t call the API: a request with an
Originheader is refused (origin_not_allowed). - Writes take an optional
idempotency_key(1–100 characters). Resending it within 24 hours returns the first answer.
curl -s https://api.switchdesk.org/v1/list_todos \
-H "Authorization: Bearer $SWITCHDESK_TOKEN" -H "Content-Type: application/json" \
-d '{"section":"today"}'
curl -s https://api.switchdesk.org/v1/add_todo \
-H "Authorization: Bearer $SWITCHDESK_TOKEN" -H "Content-Type: application/json" \
-d '{"title":"Call Ana","category":"Fernhill","due":"2026-10-14","idempotency_key":"call-ana-1"}'Responses
| HTTP | Means |
|---|---|
| 200 | Done: {"ok": true, …} |
| 201 | Created (a new to-do, category or note) |
| 202 | Waiting for the person: {"ok": true, "status": "pending_approval", "proposal_id": "…"} |
| 4xx | {"ok": false, "error", "message", "hint"}: see below |
Routes
| Route | GET too | Permission |
|---|---|---|
POST /v1/get_access | yes | none |
POST /v1/list_categories | yes | categories.read |
POST /v1/list_todos | yes | todos.read |
POST /v1/get_todo | yes | todos.read |
POST /v1/add_todo | todos.add_incoming / todos.add | |
POST /v1/update_todo | todos.edit | |
POST /v1/set_todo_state | todos.complete | |
POST /v1/delete_todos | todos.delete | |
POST /v1/add_todo_note | todos.edit (+ team.write on a shared to-do) | |
POST /v1/delete_todo_note | todos.edit (+ team.write on a shared to-do) | |
POST /v1/propose_changes | approvals.ask | |
POST /v1/ask_me | approvals.ask | |
POST /v1/get_proposal | yes | approvals.ask (or any write in Ask mode) |
POST /v1/list_proposals | yes | approvals.ask (or any write in Ask mode) |
POST /v1/create_category | categories.write | |
POST /v1/update_category | categories.write (+ team.write when shared or used by a meeting automation) | |
POST /v1/list_meetings | yes | meetings.read |
POST /v1/get_meeting | meetings.read (+ meetings.transcripts for the transcript) | |
POST /v1/search_meetings | meetings.read | |
POST /v1/list_notes | yes | notes.read |
POST /v1/get_note | yes | notes.read |
POST /v1/search_notes | notes.read | |
POST /v1/create_note | notes.write | |
POST /v1/append_to_note | notes.write | |
POST /v1/update_note | notes.write | |
POST /v1/delete_note | notes.write | |
POST /v1/get_recap | yes | recaps.read |
POST /v1/search_brain | brain.read | |
POST /v1/get_brain_page | yes | brain.read |
POST /v1/remember | brain.write |
Errors
| Error code | HTTP | CLI exit | Hint the API sends |
|---|---|---|---|
missing_key | 401 | 2 | Send the key in the Authorization header: Bearer sda_… |
invalid_key | 401 | 2 | The key is wrong or was deleted. Ask the person for a new key from SwitchDesk → Settings → AI Access. |
revoked | 401 | 2 | The person revoked this connection. Ask them for a new key. |
expired | 401 | 2 | This connection expired (90 days unused, or its end date). Ask the person for a new key. |
plan_required | 403 | 2 | SwitchDesk Pro is needed for AI access. |
capability_off | 403 | 2 | Not allowed. Turn it on in SwitchDesk → Settings → AI Access. |
needs_confirm | 403 | 4 | The person needs to confirm Meetings & Notes access for this connection with the code SwitchDesk emails them (Settings → AI Access). |
copy_off | 403 | 4 | The person needs to turn on "Share meetings & notes with your AIs" in SwitchDesk → Settings → AI Access on their Mac. |
transcripts_off | 403 | 4 | The person needs to tick "Include full transcripts" in SwitchDesk → Settings → AI Access on their Mac. |
not_found | 404 | 1 | |
unknown_tool | 404 | 1 | |
paused | 423 | 2 | The person paused this connection's changes. Reads still work; they can resume it in SwitchDesk → Settings → AI Access. |
rate_limited | 429 | 5 | Too many requests. Wait retry_after seconds. |
too_many_pending | 429 | 1 | |
incoming_full | 429 | 1 | |
try_again | 409 | 1 | The person's day just turned over (3 AM in their time zone). Send the same request again. |
duplicate_category | 409 | 1 | A category with that name exists; use update_category with its id. |
no_mac | 409 | 4 | The person's Mac hasn't connected yet. Ask them to open SwitchDesk on it. |
brain_off_for_ai | 403 | 4 | The person needs to choose what connected AIs may see in SwitchDesk → Settings → Brain. |
brain_off | 403 | 4 | The person's Brain is off. They can turn it on in SwitchDesk → Settings → Brain. |
not_available_yet | 501 | 4 | Coming soon. |
series_stop_required | 422 | 1 | This to-do repeats. Pass series: "stop" to delete it and end the series. |
ambiguous_category | 422 | 1 | Several categories match; pass category_id from list_categories. |
unknown_category | 422 | 1 | No category matched; call list_categories for the names. |
timezone_required | 422 | 1 | SwitchDesk doesn't know the person's time zone yet: pass timezone (e.g. "America/New_York"). |
invalid_timezone | 422 | 1 | Use an IANA time zone name like "America/New_York". |
invalid_repeat | 422 | 1 | Send an RRULE like "FREQ=WEEKLY;BYDAY=MO" or a rule object (see the docs). |
sub_todo | 422 | 1 | Sub-to-dos follow their parent. Move or date the parent, or send parent_id: null first. |
teammate_subtree | 422 | 1 | A sub-to-do belongs to a teammate, so the whole block can't be moved or deleted. |
nothing_to_change | 422 | 1 | Every field already has that value. |
reserved_name | 422 | 1 | Today's Todo and This Week are built in. |
repeat_not_allowed | 422 | 1 | Reopen the to-do (or wait for its review) before making it repeat. |
unknown_space | 422 | 1 | Use a space name or id from get_access (brain.spaces), or leave space out. |
secret | 422 | 2 | SwitchDesk never keeps passwords, keys, card, bank or ID numbers in the Brain. Suggest a password manager instead. |
Rate-limited answers (429) carry retry_after and a Retry-After header. Per-connection and account limits: Limits.