API

Everything in Stella Loop is reachable through /api/v1 — the CLI and the MCP server are thin clients over it, and there are no private endpoints behind either. The API is self-describing: an unauthenticated GET /api/v1 returns discovery information, and the complete OpenAPI 3.1 document is served at GET /api/v1/openapi.json.

Authentication

Send an API key as a bearer token:

curl -H "Authorization: Bearer $STELLA_API_KEY" \
  https://<your-api-origin>/api/v1/projects/ATLAS/tasks

Keys carry scopes (read < work < admin) and project grants; requests outside them return permission_denied with the missing requirement named. Every request is attributed to the key’s actor. Keys are created, rotated (with a grace window), and revoked in settings → API keys — the secret is shown once.

One envelope

Success responses share one shape, errors another:

{ "data": {  }, "requestId": "req_…", "pageInfo": { "cursor": "…" } }
{ "error": { "code": "task_already_claimed", "message": "…" }, "requestId": "req_…" }
  • Every response carries a request ID for support and correlation.
  • Collections paginate by cursor.
  • Error codes are a stable, machine-readable taxonomy shared with the CLI’s exit codes — unauthenticated, permission_denied, not_found, domain conflicts like task_already_claimed (409), rate_limited (429 with Retry-After), validation_failed.

Display-ID addressing

Human-readable IDs are first-class addresses. These are equivalent:

GET /api/v1/projects/ATLAS/epics/EPC-12
stella epic show EPC-12

SIG-n, INT-n, RPT-n, PRP-n, EPC-n, and TSK-n resolve everywhere an ID is accepted.

Idempotency and rate limits

  • Mutating requests accept an Idempotency-Key header; replays return the recorded response with an Idempotent-Replay header, so retry loops are safe. The CLI sends one automatically.
  • Rate limits are classed (read / write / expensive) with burst allowances; 429 responses carry Retry-After and rate-limit headers.

Events out

Two ways to be told instead of polling:

  • Event feedGET …/events with cursor pagination, type filters, and a wait= long-poll hold; or GET …/events/stream for server-sent events with heartbeats and Last-Event-ID lossless resume.
  • Webhooks — register HTTPS destinations, receive HMAC-signed deliveries (X-Stella-Signature over timestamp.body), verify, and process. Deliveries retry with backoff and dead-letter after repeated failure; a synchronous test ping exists for setup. Webhooks can also serve as an agent actor’s notification channel — task.assigned wakes the agent that should claim.

Gates apply here too

The API enforces exactly what the UI enforces: promotion passes the epic.promote checkpoint, merges pass epic.merge, invalid stage transitions are rejected with the legal edges named, and approval decisions respect approver policy. A held transition returns its approval reference so your automation can wait for the decision — or surface it to a human.