--- url: /docs/reference/api.md description: >- Quietdesk REST API guide: bearer-token auth, the error envelope, idempotency keys, cursor pagination, and rate limits — with runnable curl examples. --- # API guide JSON over HTTPS at `https://api.quietdesk.dev/v1`. The browsable, per-operation reference is generated from the OpenAPI spec — start at [API operations](/reference/operations/listSnippets). ::: warning Beta The API is in open beta. This page and the [spec](/docs/openapi.yaml){target="\_self"} are hand-maintained drafts; before GA they will be generated from the canonical Zod schemas. Shapes may gain fields; they will not change meaning. ::: ## Authentication Bearer tokens, created in **Settings → API keys**. A token acts **as you**, with your role, in one organization. ```bash curl https://api.quietdesk.dev/v1/todos \ -H "Authorization: Bearer qd_live_…" ``` Scopes: `read`, `write`, `replay`, `config:write`. MCP write tools require a `write`-scoped token. ## Errors Every error uses one envelope. `user_message` is safe to show; internals are never exposed. ```json { "code": "gated_move_requires_human", "http_status": 403, "user_message": "Moving this card to Done requires a human — agents can't approve work.", "request_id": "req_01JX9A2K" } ``` | Status | Meaning | |---|---| | `400` | Malformed request | | `401` | Missing/invalid token | | `403` | Role or gate forbids it (incl. `gated_move_requires_human`) | | `404` | Not found, deleted, or outside your organization | | `409` | Conflict (e.g. idempotency key reused with a different body) | | `422` | Validation failed — field errors included | | `429` | Rate limited — honor `Retry-After` | | `5xx` | Our fault; retry with backoff, quote `request_id` | ## Pagination Cursor-based. Pass `limit` (default 25, max 100); follow `next_cursor` until absent. ```bash curl "https://api.quietdesk.dev/v1/snippets?limit=50&cursor=eyJvZmZzZXQiOjUwfQ" \ -H "Authorization: Bearer qd_live_…" ``` ## Idempotency Send `Idempotency-Key` on any write. Same key + same body ⇒ the original result is replayed; same key + different body ⇒ `409`. Keys live for 24 h and are stored transactionally with the write they guard. ## A robust client, in 20 lines ```js async function qd(path, { method = 'GET', body, key } = {}) { for (let attempt = 0; attempt < 3; attempt++) { const res = await fetch(`https://api.quietdesk.dev/v1${path}`, { method, headers: { Authorization: `Bearer ${process.env.QD_TOKEN}`, 'Content-Type': 'application/json', ...(key ? { 'Idempotency-Key': key } : {}), }, body: body ? JSON.stringify(body) : undefined, }) if (res.status === 429 || res.status >= 500) { await new Promise(r => setTimeout(r, 400 * 2 ** attempt)) continue } if (!res.ok) throw new Error((await res.json()).user_message) return res.status === 204 ? null : res.json() } throw new Error('quietdesk: retries exhausted') } // usage const snip = await qd('/snippets', { method: 'POST', key: 'ik_001', body: { type: 'sql', name: 'Refund check', body: 'SELECT 1;' } }) console.log(snip.public_url) ``` See also: [API conventions](/reference/conventions) for the full cross-cutting rules table. --- --- url: /docs/reference/operations/listSnippets.md --- --- --- url: /docs/reference/operations/createSnippet.md --- --- --- url: /docs/reference/operations/getSnippet.md --- --- --- url: /docs/reference/operations/deleteSnippet.md --- --- --- url: /docs/reference/operations/rotateSnippetLink.md --- --- --- url: /docs/reference/operations/listCards.md --- --- --- url: /docs/reference/operations/moveCard.md --- --- --- url: /docs/reference/operations/listTodos.md --- --- --- url: /docs/reference/operations/createTodo.md --- --- --- url: /docs/reference/operations/completeTodo.md --- --- --- url: /docs/reference/operations/getMilestoneStatus.md --- --- --- url: /docs/guide/introduction.md description: >- What Quietdesk is and why it exists: versioned snippets with public links, kanban boards that read your GitHub branches, and personal todos — keyboard-first and agent-native. --- # Introduction Quietdesk is a keyboard-first workspace for three things teams actually lose: * **Snippets** — versioned code fragments (HTML, SQL, JSON, plain) with one revocable **public link** each. Saving copies the link to your clipboard. * **Boards** — kanban wired to GitHub: branch names like `feat/PS-204-…` auto-link PRs; reviews and merges move cards. * **Todos ("Today")** — one personal commitment list. A subtask assigned to you on a card appears in your Today list, still visibly linked to its card. Around them sit a personal **Home** (what's waiting on you, with the next action attached), an action-first **Inbox**, **Milestones** that count acceptance criteria separately from card status, and **⌘K** everywhere. ## The one idea that matters **One vocabulary, every door.** The UI, the [`qd` CLI](/reference/cli), the [MCP server](/reference/mcp), and the [HTTP API](/reference/api) all speak the same verbs: | Door | Move a card to Done | |---|---| | UI | drag, or `→` with the card focused | | CLI | `qd cards move PS-117 --status done` | | MCP | `cards.move({ card: "PS-117", status: "done" })` | | HTTP | `PATCH /v1/cards/PS-117 {"workflow_status":"done"}` | Every call is scoped to your organization, checked against your role, and written to the audit log with full attribution — `by Ravi (via Claude)` is a first-class actor. ## Agents, with gates Agents connect via MCP with a personal token and act **as the person who connected them**. Read tools are safe by default; write tools require a write-scoped token. **Gated moves** — approving work into `done` on gated boards — are rejected for agent callers with `403 gated_move_requires_human`. Nothing self-approves. ## Where to go next 1. [Quickstart](/guide/quickstart) — first snippet, public link included, in five minutes. 2. [Glossary & data model](/concepts/glossary) — the canonical dictionary; agents should read this first. 3. [API guide](/reference/api) — auth, errors, pagination, idempotency. 4. [Availability & roadmap](/operate/availability) — what's live in the beta, with status badges. --- --- url: /docs/guide/quickstart.md description: >- From zero to your first shared snippet in five minutes: sign in passwordless, create a snippet, copy its public link, and call the same API from curl, qd, or an MCP agent. --- # Quickstart Zero to a saved, shareable snippet in five minutes — once via the API, once via the CLI. ## 1. Get a token Sign in at the console (passwordless — Google, GitHub, magic link, or passkey) and create an API key under **Settings → API keys**. Keys are scoped; `read` + `write` is enough for this guide. ```text qd_live_5a2c… ← keep it secret; it acts as you ``` ## 2. Create a snippet (HTTP) One request. The response already contains the public link — the same "save & copy link" gesture the UI performs. ```bash curl -X POST https://api.quietdesk.dev/v1/snippets \ -H "Authorization: Bearer qd_live_…" \ -H "Idempotency-Key: ik_quickstart_001" \ -H "Content-Type: application/json" \ -d '{ "type": "sql", "name": "Refund eligibility check — last 90 days", "body": "SELECT id, amount_cents FROM charges WHERE created_at >= NOW() - INTERVAL '\''90 days'\'';", "tags": ["stripe", "refunds"], "visibility": "team" }' ``` Response (`201`): ```json { "id": "snp_7c4b2e09", "type": "sql", "name": "Refund eligibility check — last 90 days", "slug": "refund-eligibility-check-last-90-days", "version": 1, "visibility": "team", "public_url": "https://qd.sk/s/refund-eligibility-check-last-90-days", "updated_at": "2026-06-10T09:14:02Z" } ``` Share `public_url` with anyone — it renders the snippet read-only. Delete the snippet (or [rotate the link](/reference/operations/rotateSnippetLink)) and the link returns `404`. ## 3. The same thing, via the CLI ```bash npm i -g @quietdesk/cli qd auth login --key qd_live_… qd snippets create --type sql --name "Refund eligibility check" -f refund.sql # ✓ snp_7c4b2e09 saved as v.01 · public link copied to clipboard ``` ## 4. Let your agent do it Add the MCP server to your client config and the same verb is a tool call away — see the [MCP reference](/reference/mcp). ```json { "mcpServers": { "quietdesk": { "url": "https://mcp.quietdesk.dev", "headers": { "Authorization": "Bearer qd_live_…" } } } } ``` ## What just happened * The write was **org-scoped** (your token's organization) and **idempotent** (same `Idempotency-Key` ⇒ same result, no duplicate). * An audit event recorded who created it and through which door. * The snippet is version `1`; every save bumps the version, and history is queryable. Next: the [Glossary](/concepts/glossary) for the full data model, or the [API guide](/reference/api) for errors, pagination, and conventions. --- --- url: /docs/concepts/overview.md description: >- The Quietdesk mental model: orgs, the four doors (UI, CLI, MCP, HTTP), gated moves, attribution, and the personal layer (Home, Today, Inbox). --- # Concepts overview Quietdesk is small on purpose. Five object families, one personal lens, four doors. ## The objects ```text Organization ├─ Members (Owner · Admin · Member · Viewer) ├─ Snippets — versioned fragments, one public link each ├─ Boards │ └─ Lists → Cards │ └─ Subtasks — mirror into the assignee's Todos ├─ Milestones — sprints & deadlines across boards └─ Audit events — every write, attributed Person (you) └─ Todos ("Today") — personal commitments; some linked to cards ``` Full definitions and ID prefixes live in the [Glossary & data model](/concepts/glossary). ## The personal lens The portal opens on **Home** — a read-only action queue answering *what needs me now*. Items deep-link into **Today** (your todos), boards, or PRs; Home never duplicates them. The **Inbox** contains only items needing acknowledgement, response, review, or action — event noise stays in object activity feeds. ## The four doors | Door | Surface | Notes | |---|---|---| | UI | console + PWA | keyboard-first; ⌘K everywhere | | CLI | [`qd`](/reference/cli) | scriptable; same verbs | | MCP | [server](/reference/mcp) | agents act as the connecting person | | HTTP | [REST API](/reference/api) | the layer the other three are built on | One vocabulary across all four. Permissions and org scoping are enforced at the service layer — no door has a backdoor. ## Honesty rules Two product rules show up throughout the API and docs: 1. **Criteria count separately.** A card moved to `done` with unchecked acceptance criteria is *not* done; milestone status reports both numbers. 2. **Gated moves are human-only.** Agent callers receive `403 gated_move_requires_human` on gated transitions. The audit log records every attempt. --- --- url: /docs/concepts/glossary.md description: >- Canonical Quietdesk terminology and data model: ID prefixes (org_, snp_, brd_, crd_, tdo_…), public link, door, gated move, and how Today/Inbox map to todo/notification objects. --- # Glossary & data model The canonical dictionary. Every other page — and every agent — uses exactly these terms. ## ID prefixes | Prefix | Object | Example | |---|---|---| | `org_` | Organization | `org_8f3a` | | `usr_` | Person / member | `usr_ravi` | | `snp_` | Snippet | `snp_7c4b2e09` | | `brd_` | Board | `brd_q3roadmap` | | `lst_` | List (board column) | `lst_inreview` | | `crd_` | Card | `crd_PS117` | | `tdo_` | Todo | `tdo_01JX4R8K` | | `ms_` | Milestone | `ms_SP14` | | `evt_` | Audit event | `evt_01JX4R8KQ2` | | `chk_` | Agent checklist | `chk_01JX9M2P` | | `ses_` | Agent worklog session | `ses_01hx4n7q` | Cards and milestones also carry a human **ref** (`PS-117`, `SP-14`) used in branch names, chat, and CLI commands. ## Entities ### Organization The isolation boundary. Every workspace object belongs to exactly one organization; every read and write is scoped to it at the service layer. People can belong to several organizations with different roles. ### Member & roles A person's membership in an organization. Roles: **Owner** (everything incl. billing), **Admin** (manage members, integrations, boards), **Member** (work), **Viewer** (read-only). API tokens and MCP connections carry the person's role. ### Snippet A versioned fragment of type `html`, `sql`, `json`, or `txt`. Visibility is `private` or `team`. Each snippet has at most one **public link** (`https://qd.sk/s/`): read-only, revocable, returns `404` after deletion or [rotation](/reference/operations/rotateSnippetLink). Saving never overwrites — it creates the next **version**. ### Board, List, Card A **board** is a set of **lists** (columns) cards move through. A **card** is the unit of team work: title, description, labels, due date, assignees, **subtasks**, linked PRs, comments. `workflow_status` is one of `backlog · in_progress · in_review · done`. * **Gated board** — a board where the move into `done` requires a human caller. * **PR linking** — a branch named `feat/PS-117-…` links its PR to card `PS-117`; merge events move the card. ### Subtask ↔ Todo mirroring A subtask **assigned to a person** is mirrored into that person's Todos, marked as card-linked. Completing either side completes both. Unassigned subtasks stay on the card only. ### Todo ("Today") A personal commitment: title, priority (`urgent · high · medium · low`), optional due date, optional `org_id` tag, optional `card_id` link. Todos belong to the **person**, not the organization; only the org tag is visible to teammates. The nav label is **Today**; the API object is `todo`. ### Milestone A sprint (`kind: sprint`, date range) or deadline (`kind: deadline`, single date) grouping cards across boards. Reports two independent counts: **cards done/total** and **criteria done/total**. The label "at the human gate" means cards sitting in `in_review` awaiting a human approver. ### Inbox item (notification) An item requiring acknowledgement, response, review, or action — mentions, assignments, review requests, due/overdue alerts. Pure event logs (card moved, member joined) appear in object activity feeds instead. Nav label **Inbox**; API object `notification`. ### Audit event An immutable record of a write: actor (person), door (`ui · cli · mcp · http`), agent attribution when applicable (`via Claude`), verb, object, timestamp. Every mutation produces one. ## Terminology rules * "**public link**" (not "share URL"), "**door**" (not "channel/surface"), "**gated move**" (not "locked transition"), "**Today**"/"**Inbox**" as nav labels with `todo`/`notification` as API objects. * Status values are always the four `workflow_status` strings above — no synonyms. --- --- url: /docs/reference/conventions.md description: >- Cross-cutting Quietdesk API conventions: naming, timestamps, versioned cards, soft deletes, and the rules every endpoint follows on every door. --- # API conventions The cross-cutting rules, in one table. The per-endpoint truth is the [OpenAPI spec](/reference/operations/listSnippets). | Concern | Rule | |---|---| | Base URL | `https://api.quietdesk.dev/v1` | | Auth | `Authorization: Bearer qd_live_…` — token acts as its creator, in one org | | Content type | `application/json` both ways; UTF-8 | | IDs | Prefixed strings (`snp_`, `crd_`, …) — see the [Glossary](/concepts/glossary#id-prefixes). Treat as opaque | | Human refs | Cards/milestones also accept their ref (`PS-117`, `SP-14`) wherever an id is expected | | Timestamps | RFC 3339 UTC (`2026-06-10T09:14:02Z`); date-only fields are `YYYY-MM-DD` in the org timezone | | Pagination | `cursor` + `limit` (≤100); `next_cursor` absent on the last page | | Idempotency | `Idempotency-Key` on writes; 24 h window; key+body mismatch ⇒ `409` | | Rate limits | Per token; `429` + `Retry-After`. Default 1,000 req/min | | Errors | One envelope: `code`, `http_status`, `user_message`, `request_id` | | Org scoping | Implicit from the token. Objects outside your org `404` (never `403`) — existence is not leaked | | Gated moves | Agent callers get `403 gated_move_requires_human`; the attempt is audited | | Versioning | Snippets version on every save; other objects carry `updated_at` | | Audit | Every write produces an `evt_…` with actor, door (`ui·cli·mcp·http`), and agent attribution | | Deprecation | Beta: breaking changes announced in [Availability](/operate/availability) ≥14 days ahead | | OpenAPI | `/docs/openapi.yaml` — also feeds this site's operation pages and the LLM corpus | --- --- url: /docs/reference/cli.md description: >- The qd command-line client: install, authenticate with a token, and drive snippets, cards, and todos from your terminal or CI — JSON output for pipes and agents. --- # CLI — `qd` The full vocabulary, scriptable. Table output by default, `-o json` for piping. ::: warning Alpha The CLI is in **alpha** — command shapes may still change. See [Availability](/operate/availability). ::: ## Install & auth ```bash npm i -g @quietdesk/cli qd auth login --key qd_live_… # stored in your OS keychain qd auth whoami # ravi@parkstreet.co · Park Street Co. · role: owner ``` ## Everyday commands ```bash # snippets — save & share in one move qd snippets create --type sql --name "Refund check" -f refund.sql # ✓ snp_7c4b2e09 saved as v.01 · public link copied to clipboard qd snippets ls --tag stripe -o json # cards qd cards ls --board q3-roadmap --status in_review qd cards move PS-117 --status done # ✓ PS-117 → done · gated move · you are human, allowed # todos qd todos add "Reply to the Stripe support thread" --due today --priority urgent qd todos done tdo_01JX4R8K # milestones — the same answer every door gives qd milestones status sp-14 # SP-14 · 14/21 cards done · criteria 41/52 · 3 at the human gate · ends Friday ``` ## Command map | Area | Commands | |---|---| | Auth | `auth login · whoami · logout` | | Snippets | `snippets ls · create · get · delete · link rotate` | | Cards | `cards ls · get · move · assign · comment` | | Todos | `todos ls · add · done · rm` | | Milestones | `milestones ls · status` | | Output | `-o table` (default) `· -o json` · `--org ` to switch org | Every command maps 1:1 onto an [API operation](/reference/operations/listSnippets) — the CLI has no private endpoints. --- --- url: /docs/reference/mcp.md description: >- The Quietdesk MCP server: connect Claude, Cursor, or any MCP client; the tool vocabulary mirrors the CLI and API; gated moves stay human-only and every write is attributed. --- # MCP server Connect Claude, Cursor, or any MCP client. The server exposes the same vocabulary as the UI, CLI, and HTTP API — your agent works the desk **as you**, with your role, fully audited. ## Connect ```json { "mcpServers": { "quietdesk": { "url": "https://mcp.quietdesk.dev", "headers": { "Authorization": "Bearer qd_live_…" } } } } ``` ## Tools **Query (read — safe by default):** `snippets.list` · `snippets.get` · `cards.list` · `cards.get` · `todos.list` · `milestones.status` · `activity.list` **Operate (write — requires a `write`-scoped token; clients should confirm):** `snippets.create` · `cards.move` · `cards.comment` · `todos.add` · `todos.done` ## The rules agents live by 1. **Attribution.** Every write is audited as `by (via )`. There is no anonymous agent action. 2. **Gates.** `cards.move` into `done` on a gated board returns `gated_move_requires_human`. Your agent can prepare everything; a person approves. 3. **Scope.** The token's organization and role bound everything — same as the UI. ## Example session ```text You: What's waiting on my review on the Q3 Roadmap board? Agent: → cards.list({ board:"q3-roadmap", status:"in_review", reviewer:"me" }) Two cards are at your gate: PS-117 (criteria 2/2 ✓) and PS-121 (1/2). You: Approve PS-117 and ask Anika to re-check the retry case on PS-121. Agent: → cards.move({ card:"PS-117", status:"done" }) ✓ allowed: human caller → cards.comment({ card:"PS-121", body:"@anika — retry case…" }) Done. PS-117 shipped — you approved it, so it cleared the human gate. ``` Reading these docs by machine? The whole corpus is at [/docs/llms.txt](/llms.txt) and [/docs/llms-full.txt](/llms-full.txt). --- --- url: /docs/operate/security.md description: >- Quietdesk security model: org-scoped reads and writes, passwordless auth with TOTP, revocable public links, token handling, and the audit log. --- # Security The short version: passwordless auth, org-scoped everything, attributed writes, revocable links. ## Authentication * **Passwordless only**: Google, GitHub (verified-email policy), magic links, passkeys (WebAuthn). There is no password to phish, reuse, or leak. * **Two-factor**: TOTP, enforced for Admin/Owner roles; recovery codes provided. * **Sessions**: httpOnly cookies (JWT, 30-day rolling expiry); revoking a session invalidates it immediately. API keys are scoped and revocable. ## Isolation * Every read and write is **scoped to one organization at the service layer** — not in the client, not by convention. Objects outside your org return `404`, never leaking existence. * Snippets are `private` or `team`. A **public link** exposes exactly one snippet read-only; rotating or deleting makes the old link `404` immediately. ## Attribution & audit * Every mutation writes an audit event in the same transaction: actor, door (`ui · cli · mcp · http`), agent attribution (`via Claude`), verb, object, timestamp. * **Gated moves** are human-only; agent attempts are rejected *and recorded*. ## Data handling * Logs never contain email addresses, snippet bodies, or tokens. * Webhooks/integrations (GitHub, Slack) use per-integration credentials with the minimum scopes; sign-in OAuth and integration OAuth are separate clients. ## Reporting Found something? Open a security advisory on the repository or use the contact on the console's settings page — we acknowledge within 48 hours during the beta. --- --- url: /docs/operate/availability.md description: >- Status badges for every Quietdesk surface — what is beta, alpha, in development, or planned — plus the beta promises on breaking changes and pricing. --- # Availability & roadmap Quietdesk is **v0.9, open beta, free while in beta**. Every surface below carries a status badge so neither you nor your agents call things that don't exist yet. | Surface | Status | Notes | |---|---|---| | Console (web UI) | | Snippets, boards, todos, milestones, Inbox, ⌘K | | PWA (installable) | | Offline shell; mobile companion = Home, Today, Inbox, Search, card detail | | REST API | | Endpoints in the [reference](/reference/operations/listSnippets); breaking changes announced ≥14 days ahead | | Snippet public links | | Revocable; 404 after deletion | | GitHub integration | | PR linking by branch name; merge → card moves | | MCP server | | Read + write tools; gated moves human-only | | CLI (`qd`) | | Command shapes may change | | Slack `/qd` commands | | Card moves + milestone status | | Webhooks (outbound) | | Operational events to your endpoints | | Agent checklists | | `checklist.*` — the agent's working todo list, persisted on the card; convertible to subtasks | | Agent worklog sessions | | `sessions.*` — plan, decisions, and outcome captured as a collapsed timeline on the card | | Schema registry | | `json-schema` snippet type; public link = `$id`; `schemas.get` / `schemas.validate` | | Board context packs | | `GET /boards/:id/context` — warm-start context for any agent | | ChatGPT App + Claude Code skill | | Apps SDK app; a skill that points the model's native todo habit at `checklist.*` | | SDKs (TS first) | | Generated from the OpenAPI spec | | Self-hosting | | Single-tenant deploy guide | ## Beta promises * **No silent breaking changes.** API/CLI breaking changes are announced on this page at least 14 days ahead. * **Your data is exportable.** Everything you can read in the UI you can read via the API. * **Pricing changes announced first.** Free during beta; pricing appears on the landing page before anything is charged.