Cervello docs
☰ Contents
API reference

Cervello API

Cervello turns a bug report or support ticket into a review-ready GitHub pull request. Submit a ticket, watch the agent triage → plan → execute → draft a PR over a WebSocket stream, then approve sensitive steps and inspect the full audit trail. This reference documents every route the product UI and the embeddable widget call.

Base URL https://api.cervellolab.xyz ⧉

Authentication

When the server operator sets AGENTOS_API_KEY (or issues per-client tokens), every route except the ones below requires an X-API-Key header. If no key is configured server-side — as on the public demo — auth is bypassed entirely and every route is reachable unauthenticated.

Always open, regardless of server auth config
/api/health /api/readiness /api/widget/* /api/repo/parse /api/connectors /api/waitlist
Header
X-API-Key: your_api_key

Quickstart

Create a session from a ticket. The agent loop runs asynchronously in the background — this call returns immediately with a session id you can poll or stream.

curl
# create a session
curl https://api.cervellolab.xyz/api/sessions \
  -X POST \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $AGENTOS_API_KEY" \
  -d '{
    "title": "Checkout button unresponsive on Safari",
    "body": "Clicking Pay Now does nothing on iOS Safari 17. No console errors. Works fine on Chrome.",
    "source": "manual",
    "kind": "bug",
    "repo_label": "hooman96/cervello"
  }'

Then poll GET /api/sessions/{'{id}'} or open a WebSocket to /api/stream/{'{id}'} to watch the triage → plan → execute → PR-draft loop live.

Bring your own key

POST /api/sessions accepts an optional llm object so you can run a session on your own model provider instead of the server's default. Pass your key with each session you create — it is held in memory only for the duration of that one run, and is never persisted, never echoed back in any response, and never written to logs or the audit trail.

FieldTypeDescription
provider string One of anthropic, openai, gemini, ollama, claude-cli, mock.
api_key string? Write-only. Used for this session's run, then discarded.
model string? Optional model override for the chosen provider.
Session body — llm field
{
  "llm": {
    "provider": "anthropic",
    "api_key": "sk-ant-…",
    "model": "claude-sonnet-4-5"
  }
}

GitHub push credentials are server-side only. Opening a real pull request requires the server operator to have configured a GITHUB_TOKEN with Contents: Read & write and Pull requests: Read & write on the target repo. There is currently no per-session field or endpoint for submitting your own GitHub token — every session on a given deployment pushes through that one server-configured credential (or drafts a PR without pushing, if none is configured).

Coming soon

A saved, per-user key vault (so you don't resend a key with every session) is planned but not built. Today, BYOK is strictly per-request — there is no /api/keys endpoint.

Group

Sessions

Submit tickets, track the agent loop, and gate sensitive steps behind human approval.

POST /api/sessions requires X-API-Key

Submit a ticket and start a session. The agent loop (triage → plan → execute → PR draft) runs in a background thread; this returns immediately with the new session.

Request body
FieldTypeDescription
titlestringrequired — short ticket title.
bodystringrequired — full ticket description.
sourceenumoptional, default manual — one of zendesk, intercom, linear, jira, github, sentry, slack, manual, webhook.
kindenumoptional, default bug — one of bug, feature_request, support, question, unknown.
reporterstring?optional — name or handle of whoever filed it.
repo_labelstringoptional, default "default" — repo URL, owner/repo, or SSH form.
metadataobjectoptional — free-form key/value passthrough.
llmobject?optional — BYOK credentials, see above.
pace_secondsnumber?optional — artificial pacing between loop cycles (demo pacing); 0 runs as fast as the LLM allows.
submitter_idstring?optional — override the submitter identity normally resolved from the API key.
Response — 200
FieldTypeDescription
idstringSession id, e.g. sess_….
repo_labelstringResolved repo label.
statusenumpending, planning, executing, paused_for_approval, pr_drafted, completed, failed, canceled.
created_at / updated_atstringISO-8601 timestamps.
project_idstring?Multi-tenant project this session belongs to, if any.
submitter_idstring?Resolved submitter identity.
Request
curl https://api.cervellolab.xyz/api/sessions \
  -X POST \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $AGENTOS_API_KEY" \
  -d '{
    "title": "Checkout button unresponsive on Safari",
    "body": "Clicking Pay Now does nothing on iOS Safari 17.",
    "source": "manual",
    "kind": "bug",
    "repo_label": "hooman96/cervello"
  }'
Response · 200
{
  "id": "sess_8f2c1e0a",
  "repo_label": "hooman96/cervello",
  "status": "pending",
  "created_at": "2026-08-11T14:02:03Z",
  "updated_at": "2026-08-11T14:02:03Z",
  "project_id": "proj_4a1d",
  "submitter_id": "legacy"
}
GET /api/sessions requires X-API-Key

List recent sessions, newest first.

Query params
FieldTypeDescription
limitintegeroptional, default 50.
Response · 200
{
  "sessions": [
    {
      "id": "sess_8f2c1e0a",
      "repo_label": "hooman96/cervello",
      "status": "pr_drafted",
      "created_at": "2026-08-11T14:02:03Z",
      "updated_at": "2026-08-11T14:03:41Z"
    }
  ]
}
GET /api/sessions/{session_id} requires X-API-Key

Full session detail: current status, the PR draft (if any), per-session config, post-PR pipeline results, and clone metadata. Powers the product UI's session view.

Response — 200
FieldTypeDescription
sessionobjectSame shape as the session in POST /api/sessions.
pr_draftobject?Drafted PR: title, body, branch_name, base_branch, files_changed.
configobjectpace_seconds, repo_canonical_url.
pipelinearrayPost-PR pipeline stage results (CI, preview deploy, etc.).
artifactsobjecte.g. github_pr_url, preview_url.
repo_cloneobject?Shallow-clone metadata when a repo was cloned for code-aware planning.
Response · 200
{
  "session": {
    "id": "sess_8f2c1e0a",
    "repo_label": "hooman96/cervello",
    "status": "pr_drafted",
    "created_at": "2026-08-11T14:02:03Z",
    "updated_at": "2026-08-11T14:03:41Z"
  },
  "pr_draft": {
    "title": "Fix: guard checkout submit against double-tap on iOS Safari",
    "branch_name": "agentos/8f2c1e0a",
    "base_branch": "main",
    "files_changed": [{ "path": "src/checkout/PayButton.tsx" }]
  },
  "config": { "pace_seconds": 0, "repo_canonical_url": "https://github.com/hooman96/cervello" },
  "pipeline": [{ "label": "CI", "outcome": "success" }],
  "artifacts": { "github_pr_url": "https://github.com/hooman96/cervello/pull/142" },
  "repo_clone": null
}
POST /api/sessions/{session_id}/approvals/{action_id} requires X-API-Key

Grant or deny a pending approval. The policy engine pauses sensitive actions (writes in sensitive sessions, deletes, dependency changes) here until a human decides.

Request body
FieldTypeDescription
decisionenumrequired — grant or deny.
bystringoptional, default "operator" — who made the call.
Request
curl https://api.cervellolab.xyz/api/sessions/sess_8f2c1e0a/approvals/act_3b7f \
  -X POST \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $AGENTOS_API_KEY" \
  -d '{"decision": "grant", "by": "hooman"}'
Response · 200
{
  "action_id": "act_3b7f",
  "verdict": "granted",
  "by": "hooman"
}
GET /api/approvals/pending requires X-API-Key

Action ids currently awaiting a human decision, across all sessions.

Response · 200
{
  "action_ids": ["act_3b7f", "act_91ae"]
}
Group

Live updates

Stream the agent loop live, or replay it from the append-only audit log.

WS /api/stream/{session_id} requires X-API-Key*

Live WebSocket feed of audit events for one session. On connect, replays every event so far, then streams new ones as the agent loop progresses (planner → policy → executor → observer → critic → memory).

* the auth middleware only gates /api/ HTTP requests today; treat the session id itself as the access boundary for this stream.

JavaScript
const ws = new WebSocket("wss://api.cervellolab.xyz/api/stream/sess_8f2c1e0a");
ws.onmessage = (evt) => {
  const event = JSON.parse(evt.data);
  console.log(event.type, event.actor, event.payload);
};
Event shape
{
  "type": "pr_drafted",
  "session_id": "sess_8f2c1e0a",
  "actor": "agent",
  "timestamp": "2026-08-11T14:03:41Z",
  "payload": { "pr": { "title": "Fix: guard checkout submit against double-tap" } }
}
GET /api/audit/{session_id} requires X-API-Key

Full, replayable audit trail for one session — the append-only source of truth the WebSocket stream is built from.

Query params
FieldTypeDescription
limitintegeroptional, default 1000.
Response · 200
{
  "events": [
    { "type": "session_created", "actor": "system", "timestamp": "2026-08-11T14:02:03Z" },
    { "type": "ticket_triaged", "actor": "agent", "timestamp": "2026-08-11T14:02:11Z" }
  ]
}
Group

Observability

Health, readiness, operations, and per-ticket cost/budget metrics.

GET /api/health open

Liveness check + which LLM mode is active. The product UI uses this to show a DEMO MODE badge.

Request
curl https://api.cervellolab.xyz/api/health
Response · 200
{
  "status": "ok",
  "version": "0.2.0",
  "llm_mode": "mock",
  "sessions_in_memory": 214
}
GET /api/readiness open

Snapshot of whether this deployment is ready to push a real PR (real LLM configured, GitHub token present, not forced into mock mode). Never returns secrets — token presence is a boolean.

Response · 200
{
  "llm": { "provider": "MockLLM", "model": "mock-heuristic", "is_real": false },
  "github_token_present": false,
  "force_mock": true,
  "demo_pipeline": true,
  "allow_demo_push": false,
  "block_failed_pr": false,
  "real_push_ready": false
}
GET /api/dashboard requires X-API-Key

Operations summary for your sessions: status breakdown, connector status, PRs drafted, and your last 50 audit events. Scoped to the calling client.

Response · 200 (abridged)
{
  "totals": { "sessions": 214, "audit_events": 3190, "errors": 3, "prs_drafted": 97, "pipelines_run": 97 },
  "by_status": { "completed": 180, "pr_drafted": 30, "failed": 4 },
  "connectors": [{ "slug": "github", "status": "simulated" }],
  "recent_events": [{ "type": "pr_drafted", "session_id": "sess_8f2c1e0a" }]
}
GET /api/metrics/ticket/{ticket_id} requires X-API-Key

Per-ticket observability snapshot from the queue's live budget counters: token usage, call counts, multi-step cycles, subagent depth, wall time, and cost-ceiling state. Returns 404 if the ticket isn't in the queue. For replayable history, use /api/audit/{'{session_id}'} instead.

Response · 200
{
  "ticket_id": "tkt_9a12f0",
  "status": "in_flight",
  "tokens": { "prompt": 18320, "completion": 4110, "cached": 9200, "total": 22430, "max": 200000 },
  "calls": { "llm": 6, "tool": 14, "subagent": 0 },
  "multi_step_cycles": 2,
  "max_subagent_depth": 0,
  "wall_seconds": 94,
  "max_wall_seconds": 1800,
  "budget_state": "continue"
}
Group

Repo & connectors

Validate a repo reference and check the status of GitHub / CI / preview-deploy connectors.

GET /api/repo/parse open

Live-validates a repo URL as a user types it (owner/repo, https, or ssh form). Backs the repo field on the product UI's ticket form.

Query params
FieldTypeDescription
textstringrequired — the raw text the user typed.
Request
curl "https://api.cervellolab.xyz/api/repo/parse?text=hooman96/cervello"
Response · 200
{
  "ok": true,
  "ref": {
    "label": "hooman96/cervello",
    "clone_url": "https://github.com/hooman96/cervello.git",
    "canonical_url": "https://github.com/hooman96/cervello"
  },
  "hint": null
}
GET /api/connectors open

Status snapshot of every configured connector (GitHub, CI, preview deploy, merge observer). Connectors observe your existing infra — they never own it.

Response · 200
{
  "connectors": [
    { "slug": "github", "display_name": "GitHub", "status": "not_configured", "status_detail": "GITHUB_TOKEN not set" },
    { "slug": "ci", "display_name": "CI", "status": "simulated", "status_detail": null }
  ]
}
Group

Widget

The customer-facing contract consumed by the embeddable widget (widget.js). Deliberately narrow — no internals, no diffs, no audit ids.

GET /api/widget/sessions/{session_id}/digest open

Customer-safe progress view of a session, translated into a human-friendly phase and message. This is the one contract stability matters most for — customers may write code against these field names.

Response — 200
FieldTypeDescription
phaseenumqueued, triaging, working, review, pr_ready, complete, failed, canceled, unknown.
messagestringOne human-readable sentence for the current phase.
triageobject?actionability + one sentence of reasoning. No internal detail.
probject?title + url only — never body, diff, or files.
preview_urlstring?Live preview deploy link, if the pipeline produced one.
pipelinearrayStage label / outcome / url only.
costobject?Only present for real, non-zero LLM spend — cost_usd, total_tokens, cache_read_tokens.
Request
curl https://api.cervellolab.xyz/api/widget/sessions/sess_8f2c1e0a/digest
Response · 200
{
  "session_id": "sess_8f2c1e0a",
  "phase": "pr_ready",
  "message": "A pull request has been drafted and is awaiting reviewer approval.",
  "title": "Checkout button unresponsive on Safari",
  "created_at": "2026-08-11T14:02:03Z",
  "triage": { "actionability": "actionable", "reasoning": "Reproducible UI bug with a clear component to fix." },
  "pr": { "title": "Fix: guard checkout submit against double-tap", "url": "https://github.com/hooman96/cervello/pull/142" },
  "preview_url": "https://cervello-pr-142.preview.app",
  "pipeline": [{ "label": "CI", "outcome": "success", "url": null }],
  "cost": null
}
Group

Waitlist

Early-access signup, used by the marketing demo at /demo.

POST /api/waitlist open

Register an email for early access. Deliberately narrow response — never reveals whether an email is already registered, to avoid enumeration.

Request body
FieldTypeDescription
emailstringrequired.
Request
curl https://api.cervellolab.xyz/api/waitlist \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"email": "jamie@northwind.io"}'
Response · 200
{ "ok": true }