Skip to content

API overview

The portal’s API is the JSON-over-HTTP API that the web app uses. Every path starts with /api. The endpoint pages under Endpoints in the sidebar are generated from the backend’s OpenAPI schema.

The API uses the same session cookie as the web app, factory_session. There are no API tokens yet.

  1. Open GET /api/auth/login in a browser. It redirects to GitHub, then back to GET /api/auth/callback, which sets the cookie.
  2. Send the cookie with every request. In the browser this happens on its own, since the web app and the API share an origin.

Native apps get the same cookie through a PKCE exchange instead: GET /api/auth/login?app_challenge=… redirects back to the app with a code, which POST /api/auth/app/session exchanges for the cookie. See Security.

A request without a valid session gets 401 {"detail": "Not signed in"}.

Requests act on the viewer’s current workspace. Change it with POST /api/auth/workspace, and read it from GET /api/auth/me.

Topic Convention
Bodies JSON, snake_case fields
IDs UUID strings, except numeric GitHub IDs such as repo_id and github_repo_id
Times ISO 8601 with a time zone
Create 201 with the created object
Delete 204 with no body
Errors {"detail": "A sentence for a person"}. Validation errors (422) use FastAPI’s list form.
Other workspaces A row in another workspace answers 404, as if it did not exist
Streams GET /api/threads/{id}/events is Server-Sent Events, not JSON: id is the event’s seq, event is agent, and data is the event. Resume with ?after= or Last-Event-ID.
Code Meaning
400 The request is valid but not allowed now, for example removing the default repository
401 Not signed in
403 Signed in, but the role does not allow it
404 Not found in this workspace
409 Conflict: a duplicate name or invite, the sandbox is not running, or the agent is not connected
422 Invalid input
501 The sandbox provider does not support this feature
502 GitHub or the sandbox provider failed

Most routes return plain dictionaries, so the generated reference shows request bodies and parameters in full, but responses only as object. The TypeScript types in frontend/src/api.ts describe the responses the web app relies on. Adding Pydantic response models to a route makes its response appear here.

The running backend also serves FastAPI’s own Swagger UI at /docs and the schema at /openapi.json on the API port (8000 by default). Those reflect the code you are running, while this site shows the schema committed in docs/public/openapi.json.

After any change to a route, its parameters or its models:

Terminal window
cd docs
pnpm openapi

This runs scripts/export_openapi.py with the backend’s Python environment and rewrites docs/public/openapi.json. The script adds two things the app does not declare: the local server URL for the request examples, and the factory_session cookie on every route that answers 401 without it. It finds those routes by calling each one without a cookie, so it needs no database. Commit the file with the API change.