Appearance
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.
Beta
The API is in open beta. This page and the spec 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 for the full cross-cutting rules table.