Documentation for v2.6.1. Read the latest, v2.12.0.

Reference

Using the API

The two generated references list every route: the REST API that build plugins and CLIs speak (api.yaml) and the Maintenance API behind the admin pages (maintenance.yaml). This page is the prose around them: how a call authenticates, what a token may do, and what an error looks like.

In one minute#

Sessions and API tokens#

Sanshain knows two credentials, and both travel in the same header. A session token is what POST /auth/login answers ({"token": "…"}) and what the web UI sends with every call; it expires with the session. An API token is long-lived, belongs to one user and is meant for CI, scripts and AI clients. Users mint their own on the account page (/account.html) or with POST /auth/tokens; the raw token is shown once, expires_in_days is mandatory, and the listing shows when each token last authenticated a request.

Which credential a route takes is stated on every entry of the generated pages:

Routes Session token API token
Provide, require, catalog push and version listings (REST API), the reports, /endpoint-versions and the update streams accepted accepted; the scope decides what it may do
/auth/tokens (minting and revoking tokens) required refused: a token cannot mint tokens
Maintenance API (/admin/*) and the UI's own endpoints (/auth/*, the dashboard, the audit timeline) required refused with 401 and an ErrorBody, whatever the scope: these routes validate sessions only. The one exception is GET /admin/backup/sqlite, which also admits a full token for the scheduled backup and refuses a read_only one with 403.
/mcp (AI assistants) refused required; prefer read_only
/validate, /version, /health, /ready no authentication

Scopes: full and read-only#

A token has a scope, decided when it is minted and immutable afterwards (ApiTokenScope):

Scope May Refused with 403
full Everything its owner may do. —
read_only GET /report and its variants, /producers/{producername}/versions, /endpoint-versions, the SSE and WebSocket update streams, the MCP endpoint. Provide (all types, including retire), /require, /require/asyncapi, /require/grpc, /require-bundle, PUT /catalog/{systemname}, and GET /admin/backup/sqlite. The body names the token's scope and the scope the call needs.

The reference marks these routes session or API token (full); routes a read-only token may call are marked session or API token (full or read-only). Tick Read-only (for AI / MCP clients) on the account page, or send "scope": "read_only", to mint one.

Independently of the scope, publishing a GA version (stability: ga) needs the release_ga permission of the token's owner, the releaser role; admins and root hold it implicitly. Retiring a family needs the same permission or maintainership of the Producer. On the maintenance side the reference shows, per route, the permission its contract description names and whether a Producer's maintainers are admitted too; a route whose description names none is marked not stated there, and the router is the authority for it.

Error shapes#

Errors raised by the application (AppError) answer JSON in the shape of ErrorBody: error carries the human-readable message, proposed_version appears only on a 409 from the version rules and is the next free version to publish as instead. Not every refusal reaches the application; the exceptions are listed with their status below.

{ "error": "GA version 1.2.0 exists with different content", "proposed_version": "1.3.0" }

A successful call can still carry a Sanshain-Warning header, once per warning, for things a build should print without failing: an unknown tag, trunk and tag together, a catalog push that recorded nothing.

Limits and caching#

Reports and graph scopes#

GET /report returns the dependency report; ?scope=main[@instant] or ?scope=<branch>[@instant] replaces the graph with that pin set (dev, the default, is the accumulated activity). The same parameter applies to /report/markdown and /report/isolation. The report routes are not in api.yaml (see below), so this parameter is documented only here and in The web UI.

Not in the contract#

The REST reference lists what api.yaml documents. These routes take an API token as well (api_auth in the router, read-only tokens accepted) or a session, but the contract does not list them, so the generated page cannot show them:

Names#

The two roles are Producer (provides a spec) and Consumer (requires endpoints). Only the canonical field names producername and consumername are accepted; the 1.x aliases servicename and clientname, and the branch-era fields branch, base_version, force, timeout and pull_from_branch, are rejected as unknown fields.

Admin routes are not aliased either: /admin/services* became /admin/producers*, /admin/clients* became /admin/consumers*, and /admin/nuke/services / /admin/nuke/clients became /admin/nuke/producers / /admin/nuke/consumers. The old paths answer 404.

Developer Mode#

For local testing only, Developer Mode bypasses authentication; it needs both the switch and the ALLOW_INSECURE_DEV_MODE=true safety gate and fails closed otherwise. See Developer mode.