API Design
The conventions every AuthSpoke API follows — resource-oriented, versioned, fully headless, and predictable for any client.
Every AuthSpoke API is designed assuming another system — an AI agent, an SDK, the CLI, CI, an automation — will consume it, not just a human writing HTTP by hand. The API is fully headless: everything the console can do is reachable over the same REST surface with a bearer token.
Authentication (headless)#
Two token types, one API:
| Caller | Grant | Endpoint |
|---|---|---|
| Human / admin | Username + password → JWT | POST /api/v1/auth/signin |
| Machine (agent, MCP server, CI, server-to-server) | API key → client-credentials JWT | POST /api/v1/auth/token |
Send it as Authorization: Bearer <token>. Tenant context is resolved from the token — no per-tenant subdomain required on the headless host (api.authspoke.com). Auth endpoints are rate-limited per IP + identity.
Conventions#
- Resource-oriented — objects are resources (
/ai/agents,/ai-assets/{id},/platform/capabilities). State transitions that are not simple field edits use an explicit action sub-resource (POST /access-requests/{id}/approve,/access-assignments/{id}/revoke) rather than overloading the resource body. - Versioned — all endpoints live under
/api/v1. Existing versions are never broken; new behavior ships under new paths or a future version. Removals are announced with aDeprecationwindow before a new major version. PUTreplaces,PATCHedits — usePUTto fully replace a resource representation andPATCHfor partial field updates.- Permission-aware — each endpoint declares its authentication and capability requirements (shown in the API Reference).
Pagination#
List endpoints return a consistent envelope:
{ "items": [ ... ], "page": 0, "size": 50, "totalItems": 128, "totalPages": 3 }
Pass ?page= and ?size= to page. Endpoints that return a naturally bounded set (e.g. the connectors available in a workspace) return { "items": [...], "totalItems": N } without page fields.
Filtering & sorting#
Filters are query parameters named for the field they filter (?environment=PRODUCTION&governanceState=GOVERNED&q=<search>), combined with AND. Sorting uses ?sort=<field>. Unknown filter values are ignored rather than erroring.
Status codes#
| Code | Meaning |
|---|---|
200 | Success |
201 | Resource created (returns the new resource) |
204 | Success, no body (e.g. delete) |
400 | Invalid request (bad/missing fields) |
401 | Missing/invalid token |
403 | Authenticated but not permitted |
404 | Resource not found |
409 | Conflict (duplicate / integrity) |
429 | Rate limited |
Errors#
Every error is the same JSON shape — see Errors:
{ "timestamp": "2026-07-25T07:10:05Z", "status": 400, "error": "Bad Request", "message": "Username and password are required." }
Server faults return a generic message; the real cause is logged server-side and never leaked to the caller.
OpenAPI is the contract#
The API Reference is rendered from the canonical OpenAPI specification, which is generated from the live controller surface by scripts/generate-openapi.mjs (it scans every Spring controller and merges full coverage into the hand-authored schemas). Regenerating after a backend change exposes new endpoints automatically, so the reference stays complete and never drifts. You can download the spec and generate clients, mocks or tests from it.
Roadmap#
Planned additions the design already anticipates: a richer hypermedia envelope (links, relationships), cursor pagination for high-volume lists, and backend-native OpenAPI generation (springdoc) to replace the controller-scan generator.