{"name":"Telos API","version":"v1","description":"Outcomes-anchored project management API. The chain: Vision → Objective (anchored to a Metric) ← Opportunity (M:N) → Task. Metrics inform Objectives; Opportunities tie to one or more Objectives with an optional suspected delta. Each entity has a materialised visionPath tracing it to the root vision. Projects retired 2026-05-19 — Opportunity owns the full lifecycle, Tasks carry the engineering spec.","baseUrl":"/api/v1","authentication":{"method":"Bearer token","header":"Authorization: Bearer telos_live_<key>","note":"API keys carry a role (engineer, pm, em, executive, admin) that determines field visibility. Leadership roles see financial fields (estimatedCost, salaryMonthly, weeklyHours); engineers do not. ROI-style €/yr derivations were retired with metrics.marginalValue."},"resources":[{"name":"visions","description":"Top-level strategic container. An org typically has one active vision.","operations":["list","create","get","update"],"keyFields":["narrative","principles","version"],"children":"objectives"},{"name":"objectives","description":"Strategic objectives under a vision. Each objective anchors to a metric with a target value.","operations":["list","create","get","update"],"keyFields":["title","description","targetValue","deadline","visionId"],"parent":"visions","children":"metrics","createRequires":["title","visionId"]},{"name":"metrics","description":"Measurable indicators tied to objectives. Track currentValue against targets, in the metric's own unit.","operations":["list","create","get","update"],"keyFields":["name","currentValue","unit","dataSourceType","stewardId"],"units":["percentage","count","currency","ratio","duration","custom"],"createRequires":["name","unit"]},{"name":"opportunities","description":"Problems-to-solve and their committed lifecycle. Tied to one or more Objectives (M:N) with optional signed delta on the objective's metric. Every opportunity ships with an explicit workflow chosen at create-time. Financial fields are ACL-gated to leadership.","operations":["list","create","get","update","link-objective","reject-via-workflow"],"keyFields":["title","state","estimatedCost","workflowId","visionPath","queuePosition","dueDate"],"states":["active","completed","cancelled"],"stateFlow":"active → completed | rejected (derived from the current workflow step's terminal_kind; rejection is a jump from any active step)","children":"tasks","createRequires":["title","workflowId"]},{"name":"tasks","description":"Atomic units of engineering work. Carry the engineering spec body (content). Attach directly to an opportunity, or stand alone for bug fixes / maintenance (orphan tasks are first-class).","operations":["list","create","get","update"],"keyFields":["title","status","priority","opportunityId","content"],"statuses":["backlog","todo","in_progress","done","cancelled"],"priorities":["low","medium","high","urgent"],"statusFlow":"backlog → todo → in_progress → done | cancelled","parent":"opportunities","children":"assignments","createRequires":["title"]},{"name":"teams","description":"Sub-org groups (squads/pods). Users belong to N teams within an org.","operations":["list","create","get","update"],"keyFields":["name","description"],"children":"members","createRequires":["name"],"subResources":{"members":{"description":"Team membership. GET lists members, POST adds, DELETE removes.","operations":["list","add","remove"],"route":"/teams/:id/members[/:userId]"}}},{"name":"users","description":"Org members. Salary fields (salaryMonthly, salaryCurrency, weeklyHours) are ACL-gated to leadership roles. No create — users come from auth.","operations":["list","get","update"],"keyFields":["name","email","orgRole","salaryMonthly","salaryCurrency","weeklyHours"],"note":"orgRole comes from org membership (engineer, pm, em, executive, admin). Salary fields hidden for engineer role."},{"name":"task-assignments","description":"Multi-user task assignments with per-person effort estimates. Managed as sub-resource of tasks.","route":"/tasks/:taskId/assignments[/:userId]","operations":["list","assign","update","remove"],"keyFields":["taskId","userId","estimatedHours"],"note":"POST to assign, PATCH :userId to update estimate, DELETE :userId to unassign."},{"name":"strategies","description":"Versioned org strategies, owned by individuals (CTO, CPO, founder, etc.). Single markdown blob content. Strategy serves the vision — vision is the parent. Per-strategy ACL: only the owner (or admin) can edit; anyone can read; anyone can create their own.","operations":["list","create","get","update","publish","archive","list-versions"],"keyFields":["name","content","version","status","ownerId"],"statuses":["draft","active","archived"],"statusFlow":"draft → active → archived","createRequires":["name","content"],"note":"Updating content auto-bumps the version and creates a strategy_version snapshot. Notifications fan out to all org members. AI summary generated async.","subResources":{"versions":{"description":"Read-only version history snapshots.","route":"/strategies/:id/versions","operations":["list"]},"publish":{"description":"Transition draft → active.","route":"/strategies/:id/publish","operations":["post"]},"archive":{"description":"Transition active → archived. Archived strategies are read-only.","route":"/strategies/:id/archive","operations":["post"]}}},{"name":"notifications","description":"Per-user notifications. Each user sees only their own. Currently fired by strategy changes; more sources coming.","operations":["list","get-unread-count","mark-read","mark-all-read"],"keyFields":["type","title","body","linkUrl","readAt"],"routes":{"list":"GET /notifications?unreadOnly=true","unreadCount":"GET /notifications/unread-count","markRead":"POST /notifications/:id/read","markAllRead":"POST /notifications/read-all"}}],"conventions":{"envelope":"All responses: { ok, data, meta: { requestId, timestamp }, links, actions? }. Errors: { ok: false, error: { code, message, suggestion? } }","pagination":"Cursor-based. Pass ?cursor=<opaque_token>&limit=50 (max 200). Response includes pagination.cursor and pagination.hasMore.","filtering":"Query params: ?status=draft&priority=high,urgent. Operators: .gte, .lte, .gt, .lt, .startsWith (e.g., ?createdAt.gte=2026-01-01)","sorting":"?sort=field:asc,field2:desc","idempotency":"Send Idempotency-Key header on mutations. Same key+method+path returns cached result for 24h.","batch":"POST /api/v1/batch with { operations: [{ id, method, path, body? }] }. Max 20 operations.","actions":"Responses include an 'actions' array showing valid next operations with exact method, href, and body to send.","errors":"Error codes: VALIDATION_FAILED, NOT_FOUND, ALREADY_EXISTS, INVALID_STATE_TRANSITION, FORBIDDEN, UNAUTHORIZED, RATE_LIMITED, IDEMPOTENCY_CONFLICT, INTERNAL_ERROR. The 'suggestion' field tells you exactly how to fix the error."},"ai":{"description":"AI context endpoint for terminal-based agents (Claude Code, Codex, Copilot). Returns vision context + system prompt in one call.","endpoint":"GET /api/v1/ai/context/vision/:visionId","returns":"systemPrompt (use as system message), context (current vision state + gaps), tools (REST endpoints for applying changes)","workflow":"1. Fetch context. 2. Use systemPrompt to challenge the vision with the user. 3. Apply changes via PATCH/POST endpoints."},"hierarchy":"vision → objective → metric → opportunity → task. Use nested routes (e.g., GET /visions/:id/objectives) or flat routes with filters.","rateLimits":{"default":"600 requests/minute per API key","headers":"X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset"}}