Skip to content
GitHub
Developer

API Usage

Spectral exposes a REST API via FastAPI plus an MCP-equivalent surface, served from the dedicated api.runspectral.com host (ADR-095). Paths carry no /api/ prefix (the host already scopes it, ADR-077 D7) and no version segment — the API is versioned by date: each key is pinned to the version current at its first use, with an optional Spectral-Version: YYYY-MM-DD request header to override, per ADR-091. This guide covers authentication, the decision API and its MCP equivalent, dashboard read routes, required headers, and error handling. For the full endpoint reference, start the API server and visit /docs (Swagger UI).

The decision API itself is specified in ADR-077; the MCP equivalent in ADR-088; action discoverability in ADR-089. URL path conventions align with the tenancy hierarchy per ADR-086 D7.


Terminal window
uv run --package spectral-api python -m spectral_api
open http://localhost:8000/docs # Swagger UI
open http://localhost:8000/redoc # ReDoc (alternative)

All request/response schemas are auto-generated from Pydantic models. The action discoverability endpoint at /orgs/{org_id}/domains/{domain_id}/actions per ADR-089 returns an OpenAPI 3.x document scoped to (org_id, domain_id) describing the deployed actions for that domain. Each action’s input schema is a typed, documented contract (input names with their value types and descriptions) sourced from the action’s canonical input ontology per ADR-107 — one named, typed input per real quantity, reconciled at publish so two rules that name the same quantity differently surface as a single input, not two. It is the authority, not a schema scraped from the predicate’s read keys.


/decide # POST decision API
/orgs/{org_id}/domains/{domain_id}/actions # GET action discoverability
/auth/* # Auth (non-domain)
/orgs/{org_id}/... # Org-scoped management
/orgs/{org_id}/domains/{domain_id}/... # Domain-scoped management
/operator/* # Operator-only (operations role)
MCP equivalent # per ADR-088

The MCP surface exposes the decision API as an equivalent first-class path per ADR-088 — same authentication shape, same routing rules, same response shape. The Codex pages document the HTTP form; the MCP form follows the same contract.


Every route requires authentication except:

  • POST /auth/register
  • POST /auth/login
  • POST /auth/callback
  • POST /auth/reset-password
  • GET /auth/oauth/{provider}
  • GET /health
Terminal window
# Register
curl -X POST http://localhost:8000/auth/register \
-H "Content-Type: application/json" \
-d '{"email": "dev@test.com", "password": "devpass", "org_name": "Dev"}'
# Login
curl -X POST http://localhost:8000/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "dev@test.com", "password": "devpass"}'
# -> { "access_token": "eyJ...", "user_id": "...", ... }

The JWT shape (subject identity, RFC 8693 act claim for delegation, scope set) is specified in ADR-087 D1 + D2. Pass the token as Authorization: Bearer <token> on subsequent requests.

API keys are domain-bound: minted per (org_id, domain_id) per ADR-086 D3, so the key carries both tenancy coordinates. A decision key grants read:actions (fetch action discoverability) + a decision scope (write:domain) to invoke /decide (ADR-077 D7). Because the key resolves (org, domain) server-side, the /decide body needs no tenancy — just the action + context.

Terminal window
# Create a key (requires admin:domain scope)
curl -X POST http://localhost:8000/orgs/<org_id>/domains/<domain_id>/keys \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"name": "Production agent"}'
# -> { "key": "sp_live_...", "key_id": "..." } (plaintext shown once)
# Use the key — (org, domain) come from the key, not the body
curl -X POST http://localhost:8000/decide \
-H "Authorization: Bearer sp_live_..." \
-H "Content-Type: application/json" \
-d '{
"action": "wire_transfer.release",
"context": { "amount": 50000, "payment_method": "wire", "vendor_id": "v_123" }
}'

Authorization uses action:resource scopes per the role + scope model in Access Control:

ScopeDescription
read:domainRead domain data (decisions, audit chain, System Card, World Model)
write:domainCreate / update domain data (rule candidates, source materials)
approve:modulesOperator-only — approve action modules at the enshrinement gate
admin:domainDomain settings, API keys, member management
admin:orgOrg-level management
read:actionsFetch action discoverability (API key scope)
read:operations / write:operations / admin:operations / delete:operationsSpectral staff (operations org role)

The primary surface. A caller’s agent invokes /decide before acting; Spectral returns a binding { status, work_frame, decision_metadata } per ADR-077 D3.

POST /decide
Authorization: Bearer sp_live_...
Content-Type: application/json
{
"action": "wire_transfer.release",
"context": {
"amount": 50000,
"payment_method": "wire",
"vendor_id": "v_123"
},
"world_model_version": 7, // optional; omit to use the active version
"correlation_id": "wf_482" // optional passthrough; echoed via X-Correlation-Id
}

The (org, domain) tenancy is not in the body — the domain-bound API key resolves it server-side (ADR-077 D7). correlation_id may also be sent as the X-Correlation-Id header.

context carries values for supplied attributes only — system_generated attributes (request_time, request_id, authenticated_caller) are captured by Spectral at request entry and cannot be supplied by the caller.

{
"status": "YELLOW",
"work_frame": {
"mode": "review_requested",
"next_action": "route_to_human_owner",
"allowed_actions": ["gather_evidence", "request_approval"],
"forbidden_actions": ["execute_wire_transfer", "reinterpret_policy", "override_spectral"],
"required_output": "review_packet",
"missing_evidence": ["vendor_payment_history"],
"next_human_owner": "ap_supervisor"
},
"decision_metadata": {
"matched_rules": [...],
"suppression_chain": [...],
"aggregation_outcome": "winner_takes_all_T2",
"predicate_traces": {...},
"errored_predicates": [],
"request_id": "req_a1b2c3",
"request_time": "2026-05-19T14:32:11Z",
"world_model_version": 7
}
}

The status field is one of GREEN / GREEN-SKIP / YELLOW / RED. The full five-phase decomposition of every invocation is documented in Decision Execution.

To learn what actions a domain exposes:

GET /orgs/{org_id}/domains/{domain_id}/actions
Authorization: Bearer <token>

Returns an OpenAPI 3.x document scoped to (org_id, domain_id) per ADR-089 with per-action input requirements, optional version-pin support, and customer-visible aggregation modes. Each action’s input schema is a typed, documented contract — input names with their value types and descriptions, sourced from the action’s canonical input ontology per ADR-107 — so a caller can build against it without reading the rules.


HeaderWhenExample
AuthorizationAlways (except public paths)Bearer eyJ... or Bearer sp_live_...
Content-TypePOST/PUT with JSON bodyapplication/json

For /decide invocations the (org, domain) tenancy is not in the body — it is resolved server-side from the domain-bound key; the body carries only action + context (plus optional world_model_version and correlation_id). Management routes carry tenancy in the URL path (/orgs/{org_id}/domains/{domain_id}/...). API keys are bound to a specific (org_id, domain_id) at mint time and cannot be reused across domains.


POST /auth/register -> org + first member
GET /auth/me -> user profile + org/domain list
POST /orgs/{org_id}/domains -> create a domain (operator-assisted)
POST /orgs/{org_id}/domains/{domain_id}/keys -> API key for programmatic access
GET /orgs/{org_id}/domains/{domain_id}/actions -> OpenAPI for deployed actions
GET /orgs/{org_id}/domains/{domain_id}/world-model -> current world-model version + context schema + configuration
POST /decide -> binding { status, work_frame, decision_metadata }

The agent acts on the work frame. Decisions are binding (per ADR-077); the caller cannot override.

4. Inspect decisions through the dashboard

Section titled “4. Inspect decisions through the dashboard”
GET /orgs/{org_id}/domains/{domain_id}/decisions -> recent-decisions feed
GET /orgs/{org_id}/domains/{domain_id}/decisions/{decision_id} -> detail with audit-chain trace
GET /orgs/{org_id}/domains/{domain_id}/system-card -> deployment-scoped operational record
POST /orgs/{org_id}/domains/{domain_id}/decisions/{decision_id}/review-request -> flag decision for operator review
POST /orgs/{org_id}/domains/{domain_id}/decisions/{decision_id}/noteworthy -> mark decision noteworthy

The review-request and noteworthy primitives route to the operator side as override-pattern signals. See Customer Dashboard for the v0 inspection surface.

Operator routes under /operator/* are scoped to the operations org role. Drive authoring, distillation, publication, and override-pattern triage through the Operations app.


All errors follow RFC 9457 Problem Details per ADR-006 D3:

{
"type": "https://spectral.dev/errors/not-found",
"title": "Resource Not Found",
"status": 404,
"detail": "Decision dec_abc123 not found"
}
StatusMeaning
400Bad request — invalid parameters or business rule violation
401Not authenticated — missing or invalid token
403Forbidden — authenticated but lacks required scope
404Not found — resource doesn’t exist or not in tenant
422Validation error — request body failed Pydantic validation
429Rate limited (per ADR-084 D1 multi-dimensional rate limits)
500Server error

Validation failures on the supplied context of a /decide request return status: "YELLOW" with next_action: "gather_evidence_and_retry" rather than HTTP 422 — schema validation is part of the decision contract, not a transport-level error.


Collections return a paginated wrapper per ADR-006 D3:

{"results": [...], "pagination": {"total": 42, "limit": 20, "offset": 0}}

Single items return the object directly (flat, no wrapper).

Decision responses (/decide) are the documented { status, work_frame, decision_metadata } shape per ADR-077 D3 — not the generic single-item shape.

Errors use RFC 9457 Problem Details.


The API is organized into the following resource-based router modules:

GroupPrefixDescription
Auth/auth/*Registration, login, logout, OAuth, password reset, profile
Orgs/orgs/{org_id}/*Org management, members, invites
Domains/orgs/{org_id}/domains/{domain_id}/*Domain settings, members, action registry view, World Model view, System Card
Keys.../keysAPI key lifecycle (create, list, revoke) per (org_id, domain_id)
Actions.../actionsAction discoverability per ADR-089
Decisions.../decisions/*Recent-decisions feed, per-decision audit-chain detail, review-request + noteworthy primitives
Decision API/decideThe decision API per ADR-077; MCP equivalent per ADR-088
Operator/operator/*Operator workflows — authoring, distillation, publication, override-pattern triage (requires operations org role)

For the complete list of endpoints with request/response schemas, see the Swagger UI at /docs.