API overview
The Knobs API is a JSON REST API served from a single base URL:
https://api.knobs.ioEverything the Knobs apps can do — mail, chat, docs, sheets, drive, tasks, calendar, search — is available over this API. This page covers the conventions shared by every endpoint; the per-product pages document each surface.
Quickstart
Section titled “Quickstart”- Mint an API key in the Knobs app under Settings → Security (or see API keys). The token looks like
knak_<keyId>_<secret>and is shown exactly once. - Call the API with the key as a bearer token:
curl -H "Authorization: Bearer knak_..." https://api.knobs.io/v1/users/me{ "uid": "u_9f2c…", "email": "you@yourteam.knobs.io", "secondFactor": "", "amr": ["apikey"], "onboarding": ""}- Find your workspaces, then call workspace-scoped endpoints:
curl -H "Authorization: Bearer knak_..." https://api.knobs.io/v1/users/me/workspacescurl -H "Authorization: Bearer knak_..." "https://api.knobs.io/v1/workspaces/{wid}/mail/threads?limit=10"Conventions
Section titled “Conventions”Versioning
Section titled “Versioning”All routes live under /v1. Breaking changes would bump to /v2; additive fields are not breaking. The server tolerates unknown request fields, and your client should tolerate unknown response fields.
Content
Section titled “Content”Requests and responses are JSON (application/json; charset=utf-8). File bytes never transit the API itself — Drive, doc assets, and mail attachments use short-lived signed Google Cloud Storage URLs for the actual bytes.
Authentication
Section titled “Authentication”Every call (except the anonymous auth endpoints and the public calendar feed) requires Authorization: Bearer <token>. Two bearer types exist:
- a session JWT, minted by the web/iOS clients at
POST /v1/auth/exchange; - a personal access token (
knak_…), for programmatic access.
See Authentication for which surface each reaches. A small set of credential-management routes are session-only and reject API keys.
Workspace scoping and existence hiding
Section titled “Workspace scoping and existence hiding”Workspace-scoped resources live under /v1/workspaces/{wid}/<product>/…. The gateway resolves your membership before the handler runs:
- non-members get
404— the API never confirms that a workspace (or anything inside it) exists to someone who isn’t a member; - members without a sufficient role get
403.
The same existence-hiding applies at the object level: a doc, sheet, or calendar you have no access to reads as 404, never 403.
RPC verbs
Section titled “RPC verbs”State transitions that aren’t plain CRUD use a :verb suffix on a collection path (…/mail/messages:send, …/tasks:create-from-mail) or a sub-path on an ID (…/tasks/{tid}/complete, …/domains/{did}/verify). All verb actions are POST.
Pagination
Section titled “Pagination”List endpoints take ?pageSize= (default 50, max 200) and an opaque ?pageToken= (some surfaces use ?limit= and ?cursor= — noted per endpoint); responses return the items plus a next-page token/cursor. Pagination is cursor-based, never offset-based.
Idempotency
Section titled “Idempotency”Mutating calls accept a clientMutationId body field (a UUID you generate); the service dedupes replays. Chat message sends require it. The bot API uses an Idempotency-Key header instead — see Integrations & webhooks.
Error envelope
Section titled “Error envelope”Non-2xx responses return:
{ "error": { "code": "PERMISSION_DENIED", "message": "you do not have the required access level" }}code is a stable UPPER_SNAKE enum; the HTTP status mirrors it. Canonical codes:
| Code | HTTP | Meaning |
|---|---|---|
INVALID_ARGUMENT |
400 | malformed or invalid request |
UNAUTHENTICATED |
401 | missing/invalid bearer token |
PERMISSION_DENIED |
403 | authenticated but not allowed |
ONBOARDING_REQUIRED |
403 | session still mid-onboarding — finish onboarding in the app, then re-exchange |
NOT_FOUND |
404 | missing, or exists but hidden from you |
FAILED_PRECONDITION |
409 | valid request, wrong resource state |
ABORTED |
409 | revision conflict — retry with fresh state |
RESOURCE_EXHAUSTED |
429 | rate limit or quota; honor Retry-After |
INTERNAL |
500 | server fault — safe to retry with backoff |
See Errors & rate limits for details and 429 semantics.
Identity endpoints
Section titled “Identity endpoints”| Endpoint | Description |
|---|---|
GET /v1/users/me |
Who am I — uid, canonical email, auth method (amr), onboarding state |
GET /v1/me |
Alias of GET /v1/users/me |
GET /v1/users/me/workspaces |
List my workspaces |
GET /v1/workspaces |
Alias of the workspace list |
These identity reads accept API keys, so a programmatic client can always resolve who it is and where it can act.
Notifications & devices
Section titled “Notifications & devices”Per-user (not workspace-scoped) endpoints for the in-app notification feed and push-device registration.
| Endpoint | Description |
|---|---|
GET /v1/users/me/notifications |
In-app notification feed; ?unread=true and ?limit= supported |
POST /v1/users/me/notifications/{nid}/read |
Mark one notification read |
POST /v1/users/me/notifications/read-all |
Mark everything read → {"markedRead": n} |
POST /v1/users/me/devices |
Register an FCM push token {platform, fcmToken, appVersion?, locale?, tz?} — platform is web, ios, or desktop |
GET /v1/users/me/devices |
List registered devices |
DELETE /v1/users/me/devices/{tid} |
Unregister a device token |
curl -H "Authorization: Bearer knak_..." \ "https://api.knobs.io/v1/users/me/notifications?unread=true&limit=20"