Skip to content

docs(api): Runs, Workspaces and platform tokens in the API reference - #169

Merged
sre-helmcode merged 9 commits into
mainfrom
docs/runs-api-reference
Oct 10, 2026
Merged

sre-helmcode merged 9 commits into
mainfrom
docs/runs-api-reference

Conversation

@sre-helmcode

@sre-helmcode sre-helmcode commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Dependencies deployed and verified in prod (2026-10-10): cloud-api 0.8.238 (#324, a foreign workspace uuid answers the same 404 as an unknown one), cloud-api 0.8.239 (#325, GET /api/workspaces/{uuid} accepts sk- keys and platform tokens), nan-devops #805 (only GET /api/workspaces, /api/workspaces/ and /api/workspaces/{lowercase uuid} reach origin on api.nan.builders; bearer only, no CORS). Ready to merge after review.

What

Moves the Agent runs API out of the guide and into the API reference (/docs/api, /es/docs/api, rendered by Scalar from src/data/openapi.json), and adds the read-only Workspaces endpoints and platform tokens to it.

src/data/openapi.json

  • Runs tag (on the default https://api.nan.builders/v1 server):

    • POST /runs (create): body CreateRunRequest with workspace (name or id), agent (pi | hermes), prompt (1 to 32768 bytes), cwd (default /home/nan), git_isolation (null | true | false), timeout_seconds (60..7200, default 1800), model, trigger. Optional Idempotency-Key header. Responses: 201, 200 replay, 400, 401, 403 (tier_restricted, workspace_key_not_allowed), 404, 409 (workspace_not_running, agent_not_installed, no_inference_key, idempotency_conflict), 429 (run_queue_full, rate_limited).
    • GET /runs (filters workspace, state, limit 1..100 default 20, cursor; list object with next_cursor).
    • GET /runs/{id}.
    • GET /runs/{id}/events:
      • text/event-stream: run_event / state / end frames, : hb every 15 s, Last-Event-ID resume, 30-minute stream lifetime, 10 streams per member (429 too_many_streams).
      • application/json pages: after, limit 1..1000 default 500, next_after, done.
    • POST /runs/{id}/cancel (202, or 200 when the run had already ended).
  • Schemas: Run, RunResult, RunArtifact, RunList, RunEvent (10 types, data as a oneOf of per-type schemas; message = incremental chunks with final on the last), RunEventPage, CreateRunRequest, Workspace, WorkspaceDetail, WorkspaceSlot, PlatformError. Errors on /v1/runs use the existing OpenAI Error envelope.

  • Workspaces tag: only the two public routes (path-level server https://api.nan.builders, outside /v1):

    • GET /api/workspaces: member API key or platform token.
    • GET /api/workspaces/{id}: platform token only (cloud-api accepts sk- keys on the exact list path only).

    Both say only the caller's own workspaces are returned. No session-only workspace route is documented.

  • Security:

    • The API key scheme is renamed bearerAuth to apiKey (the name Scalar shows in its selector), and its description is updated. It stays the global default, so inference operations take the API key only.
    • New platformToken scheme (nan_pat_..., platform API only, refused for inference).
    • Runs operations accept both. The overview gains a short "Platform tokens" section.
  • Sources (cloud-api origin/main):

    • internal/runs/api.go: limits, states, codes.
    • internal/runs/protocol: event kinds and data, regexes, error codes.
    • internal/handlers/runs.go and runs_sse.go: routes, body, statuses, frames.
    • internal/handlers/workspace.go and internal/workspace/models.go: the workspace view.
    • internal/middleware/auth.go and handlers.PlatformTokenAPIRoutes (#323), plus the /api group's AcceptAPIKeysOn in cmd/server/main.go.

Guide (src/content/docs/runs.mdx, docs-es/runs.mdx)

  • The API section keeps the API keys vs tokens explanation and one end-to-end example: create with curl, keep the id with jq, follow with curl -N.
  • It links to /docs/api#tag/runs, #tag/workspaces and #description/platform-tokens (or the /es/docs/api equivalents). I checked these anchor formats in the browser: Scalar sets #tag/runs when you click the tag, and both locales scroll to it.
  • The endpoint, field, error and stream tables are removed from the guide. The portal and CLI sections are unchanged.

Workspaces contract (after #324, #325, #805)

  • Both operations accept an API key or a platform token; the key inside a workspace gets 401 session_required.
  • GET /api/workspaces/{id}: a foreign workspace and an unknown id answer the same 404 {"error":"workspace not found"}; a non-UUID id is a 404 from the edge (OpenAI shape). No 403 documented.
  • 401 examples are the real /api Auth bodies: {"error":"unauthorized"}, {"error":"invalid session"}, session_required (pinned by tests).
  • Each operation notes that api.nan.builders serves it to bearer credentials only, with no cookies and no browser CORS.
  • A platform token on inference: /v1/models answers 403 token_scope_denied, /v1/chat/completions answers 401 from the gateway; the scheme says "401, or 403 token_scope_denied".

Tests

  • src/lib/openapiSpec.test.ts: the surface list now counts operations (11 inference + 5 runs + 2 workspaces), so an undocumented or extra one fails.
  • New src/lib/openapiRuns.test.ts (no OpenAPI validator among the dependencies, so it includes a small JSON Schema validator) checks:
    • tags and operationIds;
    • which operation takes which credential;
    • the body fields, limits and defaults;
    • every error code;
    • the Run, RunEvent and page shapes;
    • the SSE frames, heartbeat, resume, lifetime and stream cap;
    • that the workspace routes are on api.nan.builders with nothing else under /api, and that no secret fields are present;
    • every new example against its schema (the validator is tested on bad input too);
    • that the /api/docs text includes the new operations;
    • no em-dashes.
  • src/tests/layouts/runsDocs.test.ts:
    • the guide keeps the create + follow example;
    • its JSON is accepted by the reference schema;
    • it links to the reference;
    • the long tables are gone;
    • the reference contains the runs operations.
  • npx vitest run: 1699 tests passed. npm run build: complete.
  • Browser (dev server): /docs/api and /es/docs/api show the Runs tag with all 5 operations and the Workspaces tag with 2, and the auth selector offers apiKey and platformToken. /api/docs/api.md includes the new operations.

🤖 Generated with Claude Code

barckcode and others added 7 commits October 10, 2026 22:17
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eference

Adds the Runs tag (create, list, retrieve, events stream or JSON pages,
cancel) and a read-only Workspaces tag (list, retrieve by id) to the
OpenAPI spec, with the Run, RunEvent (ten kinds), RunResult and Workspace
schemas taken from cloud-api origin/main. Documents the platform token
(nan_pat_) as a second bearer scheme next to the API key and marks which
operations accept which. The workspace routes use api.nan.builders
outside /v1. Tests pin the surface, credentials, limits and SSE frames,
and validate every new example against its schema.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Scalar shows the scheme names in its auth selector.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The guide keeps one end-to-end example (create a run, follow it with
curl -N) and the API keys vs tokens explanation, and links to the
reference for the endpoints, fields, events, errors and credentials.
Tests check the links and that the reference has the runs operations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Only GET /runs/{id} returns the prompt: drop it from the cancel example.
- GET /runs with an unknown workspace filter is an empty page, not 404.
- GET /api/workspaces/{id} with a token: 403 forbidden or
  token_scope_denied (malformed id), never the handler's 400.
- Runs-specific 401 naming both credentials; 128 KiB body cap; the
  per-workspace cap never exceeds the per-member total.
- The guide example prints the create error instead of following
  /runs/null/events.
- Wording fixes; remove a stray screenshot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ired

The create-and-follow example no longer exits the member's shell when the
create fails: it prints the error instead of following the log. The
Workspaces 401s show session_required for keys those routes refuse, and
the runs 401 notes that the message may vary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sre-helmcode

Copy link
Copy Markdown
Contributor Author

Blocking reviews (Opus, foreground): QA APPROVED and UX APPROVED at head 939457a (after fixes for QA blockers at 696c2eb and UX blockers at 696c2eb/c06efcf). Merge only after the DevOps exposure of GET /api/workspaces[/{uuid}] on api.nan.builders is deployed and verified.

- GET /api/workspaces/{id} accepts an API key or a platform token
  (cloud-api #325); the workspace key is refused on both routes.
- A foreign workspace answers the same 404 as an unknown id (#324); a
  non-UUID id is a 404 from the edge. No 403 remains.
- 401 examples are the real /api Auth bodies (unauthorized, invalid
  session, session_required), pinned by tests.
- api.nan.builders serves these routes to bearer credentials only, with
  no cookies and no browser CORS (nan-devops #805).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sre-helmcode

Copy link
Copy Markdown
Contributor Author

Workspaces aligned with cloud-api #324/#325 and nan-devops #805. QA (Opus, foreground, checked against cloud-api origin/main b9fb33b and live api.nan.builders with fake credentials) APPROVED at head 9c2cce1.

…ering

The Workspaces operations override the spec-wide /v1 base URL with a
path-level servers entry (OpenAPI 3 allows it). The Spec type in
openapiToText did not, so astro check failed in CI; and the text rendering
served at /api/docs would have sent agents to /v1/api/workspaces. Type the
Path Item with optional servers and print the override per operation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sre-helmcode
sre-helmcode merged commit fa5bc1d into main Oct 10, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants