Brain API v1
Authentication
Section titled “Authentication”Every request carries Authorization. The API key is the authoritative team identity, so
clients do not need to configure or send a separate team identifier:
Authorization: Bearer aios_<key_id>_<secret>X-AIOS-Team: <team-uuid-or-slug> # optional, for older clientsAPI keys are aios_<key_id>_<secret> format, issued per member and shown once at creation;
the brain stores only sha256(secret). X-AIOS-Team is an optional compatibility header —
when supplied it must match the key’s team UUID or slug, and clients must omit it rather
than send an empty value. Authentication failures return 401 and are audit-logged.
Endpoints
Section titled “Endpoints”POST /api/v1/items
Section titled “POST /api/v1/items”Upsert one or more content items from a workspace push.
Request body:
{ "items": [ { "row_key": "2-work/sprint-1-retro.md", "content": "# Sprint 1 Retro\n...", "frontmatter": { "title": "Sprint 1 retrospective", "audience": "team", "date": "2025-06-01" }, "tier": "team", "checksum": "sha256:abc123..." } ]}Response:
{ "accepted": 3, "rejected": 0, "errors": [] }Errors:
422— Item hasadmintier. Admin content is never accepted. Fix theaudiencefrontmatter.401— Invalid or missing API key.403— Team not found or key not authorized for this team.
GET /api/v1/items
Section titled “GET /api/v1/items”Pull team-tier content since a given timestamp (for aios pull).
Query parameters:
| Param | Type | Description |
|---|---|---|
since | ISO 8601 | Return items updated after this time |
cursor | string | Keyset pagination cursor from previous response |
limit | int | Page size (default 50, max 200) |
Response:
{ "items": [...], "next_cursor": "eyJpZCI6MTIzfQ==", "has_more": true}GET /api/v1/tasks
Section titled “GET /api/v1/tasks”Pull task rows materialized from decision/task logs since a given timestamp. Used by aios pull to write back task updates.
Same pagination parameters as /api/v1/items.
?mode=sync-origin&project=<slug> (v1.13) returns status and assignee changes for the rows this workspace pushed. Before it existed the writeback feed was dashboard-origin only, so the brain→Linear projection was one-way and 3-log/tasks-team.md decayed silently. The mode selector is additive and defaults to the pre-1.13 behaviour, so an old client’s requests and responses are byte-identical; a new client feature-detects by checking the echoed mode (a pre-1.13 brain ignores the parameter and answers mode: "writeback") and tolerates 400/404. Sync-origin rows also carry raw_status, and the mode is paged via next_cursor.
GET /api/v1/tasks?mode=table&keys=<k1,k2,…>
Section titled “GET /api/v1/tasks?mode=table&keys=<k1,k2,…>”Check whether up to 200 specific task keys are visible to the caller without confusing
absence from a capped full-table page with non-existence. The response uses the ordinary
table feed and adds unknown_keys for requested keys that matched nothing the caller may
see.
Clients must confirm both mode === "table" and an unknown_keys array before treating the
answer as authoritative. A pre-v1.14 brain can ignore keys and return a different feed.
If the read could have truncated, unknown_keys is null, meaning “could not determine.”
Tier filtering is intentional: a key the caller cannot see is indistinguishable from one
that does not exist.
GET /api/v1/decisions
Section titled “GET /api/v1/decisions”Pull decision rows created or edited in the dashboard since a given ?since= timestamp. Used by aios pull to write dashboard decisions back into 3-log/decision-log.md. Tier-scoped — an external-tier key receives only audience: "external" rows. (Additive in v1; clients tolerate a 404 from an older brain.)
GET /api/v1/projects
Section titled “GET /api/v1/projects”List the team’s projects (team-tier keys only) so aios pull can register brain-created projects (created in the dashboard, never pushed) as local marker files. Returns { "projects": [{ "slug", "name", "brain_only" }] }. (Additive in v1; clients tolerate a 404.)
POST /api/v1/query
Section titled “POST /api/v1/query”Natural-language query over the team’s shared memory. Returns a server-sent event (SSE) stream.
Request body:
{ "q": "what decisions were made about auth in sprint 1?" }SSE event types:
| Event | Payload |
|---|---|
delta | { "text": "..." } — incremental LLM output |
sources | [{ "row_key": "...", "title": "...", "tier": "team" }] — cited items |
done | {} — stream complete |
POST /api/v1/codebases
Section titled “POST /api/v1/codebases”Ingest a codebase scan (raw metrics + an optional AM agent-readiness score). The canonical pusher is the ingestion sidecar (aios-ingest scan) — the workspace’s aios assess-codebase is offline and read-only, its sparse --push having been removed in the 2026-06-19 revision. Team-tier only — an external-tier key is rejected. (Additive in v1; clients tolerate a 404 from an older brain.)
The full raw-metrics block is required. A sparse push is rejected 422, because the metrics upsert replaces the row on (codebase_id, head_sha) and a partial write would zero existing analytics. The readiness_* fields stay optional.
Request body:
{ "codebase": { "slug": "my-repo", "full_name": "org/my-repo", "provider": "github" }, "metrics": { "head_sha": "abc123…", "has_claude_md": true, "has_agents_md": false, "skills_count": 7, "commands_count": 3, "readiness_level": "L3", "readiness_pct": 67.0, "readiness_pillars": { "testing": { "passed": 2, "total": 2 } }, "readiness_rubric_version": "1.0.0" }}The readiness_* fields are scored scanner-side and persisted as-is; the brain computes a separate heuristic agentic_score at ingest.
codebase_health (revision 1.15, optional) — a scalar-only snapshot of the repository’s workspace-governance health check, scored scanner-side, carried under metrics. Fields (all required when the object is present): schema_version, rubric_version, head_sha, score_pct (0–100), status ("pass" | "warn" | "fail"), dimensions (short dimension id → { "passed": int, "total": int }), failed_invariant_ids (short rubric ids), and measured_at (ISO-8601 UTC). Three normative rules:
- Additive — a payload without
codebase_healthremains valid exactly as before 1.15; an older brain ignores the unknown key. - Never sparse — a sender including
codebase_healthmust still send the full raw-metrics block in the same push. A health-only payload is a sparse push and is rejected422, because the metrics upsert replaces the row on(codebase_id, head_sha). - Provenance-only — the brain persists it verbatim and never recomputes it. Scalars only: no file paths, no source text, no contributor identity cross the boundary (
failed_invariant_idsare short rubric ids, not paths or findings text).
The machine-readable contract is contract/codebase-payload-1.15.schema.json in the workspace repo, with canonical parity fixtures alongside it. Revision 1.15 is doc + schema only — it enables no new push lane; the canonical pusher remains the ingestion sidecar.
Response:
{ "status": "ok", "codebase_id": "uuid", "metrics_id": "uuid", "contributions": 0, "issues": 0 }Idempotent: the codebase is keyed by (team, slug) and each scan point by (codebase, head_sha) — re-pushing the same commit updates in place.
Errors:
403(forbidden_tier) — codebase metrics are team-tier only.429— rate limit, 60 scans/min per key.
POST /api/v1/metrics
Section titled “POST /api/v1/metrics”Ingest one day’s Agentic Maturity (AM) individual snapshot from aios analyze --push. Team-tier only — an external-tier key is rejected. Only ratios and counts cross the boundary; raw session content never leaves the machine. (Additive in v1; clients tolerate a 404.)
Request body:
{ "member": "alex", "metric": "aem-individual", "date": "2026-06-19", "window_days": 1, "signals": { "delegation_ratio": 0.18, "correction_loop_avg": 1.4, "error_rate": 0.05, "cost_per_task": 0.42, "tokens_per_task": 31000, "cache_hit_rate": 0.71, "tool_diversity": 9.0, "verify_tool_rate": 0.22, "subagent_usage": 0.06 }, "provisional": { "spine": "L3", "axes": { "verification": 3.1, "context_hygiene": 3.8, "autonomy": 2.4, "learning": 3.0, "cost_governance": 3.6 } }, "ce_band": 3, "sessions": 41, "tasks": 137}provisional is the client’s local placement (provenance only); the brain recomputes the canonical axes/Spine from signals so team rollups have a single authority. member is optional and defaults to the key’s member.
v1.3 (additive): optional ce_band — integer 0–4 or omitted/null. A coarse cognitive-ergonomics shadow band scored client-side relative to the operator’s own baseline (higher = more protected attention). Provenance-only: the brain persists it verbatim and never recomputes it. Older CLIs omit it; an older brain ignores it. Together with signals and provisional, this scalar is the entire privacy surface — the four raw attention signals (focus_block_avg_min, context_switch_rate, interrupts_per_hour, concurrent_sessions_peak) never cross the boundary.
Response:
{ "status": "ok", "snapshot_id": "uuid", "member_id": "uuid", "canonical": { "spine": "L3", "axes": { "verification": 3 } } }Idempotent per (team, member, date, metric) — re-pushing the same day updates in place.
Errors:
403(forbidden_tier) — agentic-maturity metrics are team-tier only.422— suppliedmemberis not on the caller’s team.429— rate limit, 60 snapshots/min per key.
Row keys
Section titled “Row keys”row_key is the relative path from the workspace root (e.g. 2-work/sprint-1-retro.md). It is the stable identifier used for diff-sync — a push with the same row_key and a new checksum updates the existing record; same checksum is a no-op.
Rate limits
Section titled “Rate limits”Rate limits are enforced per-member and per-team in the query_log table. Defaults:
| Scope | Limit |
|---|---|
| Per-member daily query cost | Configurable via DAILY_MEMBER_CAP_USD |
| Per-team daily query cost | Configurable via DAILY_TEAM_CAP_USD |
| Sync writes per minute | 60 items/min per key |
Tier vocabulary
Section titled “Tier vocabulary”| Friendly name | Canonical (wire format) | Accepted by brain? |
|---|---|---|
private, personal | admin | No — 422 |
team | team | Yes |
client, company | external | Yes |