API reference
HTTP surface of the Managed Agent API: OpenAI Responses–style agents, workspaces, browser auth, and API keys. API routes live under the path prefix /v1 on the host you choose (see below); /healthz is at the host root.
Base URL
The base URL for clients is an origin only: https:// (or http://) plus hostname and optional port. It does not include a /v1 suffix — every endpoint path in this reference already starts with /v1/ or /healthz. Example: GET https://api.example.com/v1/models.
Production layout (public nginx edge in this repo)
- API subdomain —
api.<your-domain>serves/v1/*and/healthz. For the default domain in deployment config, that ishttps://api.agentsway.dev. Use this origin asAGENT_API_BASE_URLin SDKs and for server-side or command-line clients. - Marketing / console hosts —
www.<domain>,<apex>, andconsole.<domain>are routed to the playground, but the same edge also reverse-proxies/v1/*and/healthzto the gateway so browser code can call the API same-origin (for examplehttps://www.agentsway.dev/v1/…). That is convenience for the site, not a different API product — the dedicated API host remainsapi.<domain>.
Local Docker / dev: use whatever origin your edge publishes (see deployments/docker-compose); the gateway process itself listens on api-gateway:8080 behind nginx, not on a public URL unless you expose it.
Conventions
- Unless noted, requests and responses use JSON with
Content-Type: application/json. - Authenticated endpoints expect
Authorization: Bearer <token>. Pass a workspace API key (sk-…) or a short-lived access JWT from the browser auth flow. - The gateway may attach
X-Request-ID; echo it when reporting issues or when debugging client-side correlation. - Errors use a stable envelope:
{ "error": { "type": "api_error", "code": "unauthorized", "message": "…", "request_id": "…" } }
OAuth-style scopes
Protected routes check bearer identity and optionally require scopes. Omitting scopes in this table means any valid session or API key works; specific operations may still enforce role checks upstream.
| Scope | Typical use |
|---|---|
| responses:create | POST /v1/agent, POST /v1/responses |
| responses:read | GET response by id, /children, /events |
| responses:cancel | POST /v1/responses/{response_id}/cancel (abort active run when it executes on this agent-core replica) |
| models:read | GET /v1/models |
| api_keys:read | List and read metadata for API keys |
| api_keys:write | Create, activate, deactivate, delete API keys |
| workspace_members:read | Workspaces, members, invitations, usage |
| workspace_members:write | Create or mutate workspaces, members, invitations |
Health
| Method | Path | Auth | Request | Response |
|---|---|---|---|---|
| GET | /healthz | None | — | JSON { "status": "ok" } |
Models and presets
| Method | Path | Auth | Request | Response |
|---|---|---|---|---|
| GET | /v1/models | Bearer · models:read | — | { "object": "list", "data": [{ "id", "object": "model", "owned_by", "capabilities": { provider, streaming, tools, reasoning, … } }] } |
| GET | /v1/presets | Bearer | — | { "object": "list", "data": [{ "preset", "prompt_version", "preset_metadata"?, "policy"?: { plan_mode_preference, sub_agent_preference } }] }. Requires config-core; otherwise 503. |
Agent responses
POST /v1/agent and POST /v1/responses accept the same body: OpenAI Responses–style CreateResponseRequest (input required; supply model/models or preset). Extensions include managed memory, plan_mode_preference, and sub_agent_preference. Persisted bodies may include lineage fields such as parent_response_id and root_response_id.| Method | Path | Auth | Request | Response |
|---|---|---|---|---|
| POST | /v1/agent · /v1/responses | Bearer · responses:create | JSON CreateResponseRequest. Set stream: true for SSE; multimodal limits apply to inline images. | 200 JSON Response (status completed | failed | in_progress | …) or text/event-stream: repeated data lines with JSON event objects (same public event shapes as polling). Errors on the wire use an SSE error event. |
| GET | /v1/responses | Bearer · responses:read | Query: limit (1–100, default 20), page_token (opaque cursor from prior page) | { "object": "list", "data": [{ "id", "status", "created_at", "completed_at", "model", "preset", "input_preview", "root_response_id", "background" }], "has_more", "next_page_token?" } · top-level runs only. |
| GET | /v1/responses/{response_id} | Bearer · responses:read | Path id pattern resp_* | Stored Response JSON (output array, usage, metadata, …). |
| POST | /v1/responses/{response_id}/cancel | Bearer · responses:cancel | Empty JSON body acceptable (no fields required) | { "interrupted": true | false }; true means this replica had an executing run and asked it to stop. Idempotent. |
| GET | /v1/responses/{response_id}/children | Bearer · responses:read | — | { "object": "list", "data": [{ "id", "status", "created_at", "completed_at", "root_response_id", "model" }] } |
| GET | /v1/responses/{response_id}/events | Bearer · responses:read | Query: after_sequence (optional int≥0), view = "timeline" (default) | "full" | { "data": [ { "type", "sequence_number", optional response snapshot, deltas, reasoning fields, usage, … } ] } · only public/event types appear (internal audit channels are stripped). |
Browser auth (no Bearer on these routes)
| Method | Path | Auth | Request | Response |
|---|---|---|---|---|
| POST | /v1/auth/signup | None | { "email", "password" · 8–32 ASCII (A-Za-z0-9 + symbols !@#$%^&*()-_=+[]{};:,.?/~), "display_name"? } | { "user_id", "email", "verification_required", "code_expires_at" } |
| POST | /v1/auth/verify_email | None | { "email", "code" } | AuthSession JSON; Set-Cookie refresh when configured. |
| POST | /v1/auth/signin | None | { "email", "password" } | AuthSession JSON; Set-Cookie refresh when configured. |
| POST | /v1/auth/refresh | None (needs refresh cookie) | — | Rotated AuthSession JSON; rotated Set-Cookie when configured. |
| POST | /v1/auth/signout | None | — | AuthStatus { "status", "message"? } |
| POST | /v1/auth/password_reset/request | None | { "email" } · emails a single-use **reset link** (token in URL) only when the account exists **and** email is verified | AuthStatus · message e.g. reset link sent wording |
| POST | /v1/auth/password_reset/confirm | None | { "token", "new_password" } · `token` from emailed link query string; `new_password` same policy as signup / POST /v1/me/password | AuthStatus |
| GET | /v1/auth/oauth/{provider}/start | None | Path provider: google | github; query redirect_url? | { "authorization_url", "state", "expires_at" } |
| GET | /v1/auth/oauth/{provider}/callback | None | Query code, state, redirect_url? | AuthSession JSON |
AuthSession fields include access_token, token_type (always bearer), access_token_expires_at, workspace id/role, and string scope list.
Identity and workspace APIs
| Method | Path | Auth | Request | Response |
|---|---|---|---|---|
| GET | /v1/me | Bearer | — | { "object": "identity", "user_id", "workspace_id", "workspace_name"?, "workspace_role", "api_key_id", "scopes": [] } |
| GET | /v1/me/profile | Bearer | — | { "object": "user_profile", "user_id", "email", "display_name", "email_verified", "has_password": boolean } |
| PATCH | /v1/me/profile | Bearer | { "display_name": string } · replaces profile display name for the authenticated user in the active workspace | Updated user_profile object |
| POST | /v1/me/password | Bearer | Signed-in console: rotate or add via `current_password`/`new_password`; unauthenticated reset uses emailed **link** + `POST /v1/auth/password_reset/confirm` with `token`. | { "status": "ok", "message": string } · e.g. "password added" or "password updated" |
| GET | /v1/workspaces | Bearer · workspace_members:read | — | { "object": "list", "data": Workspace[] } |
| POST | /v1/workspaces | Bearer · workspace_members:write | { "name" }; optional slug, type (personal | team | organization), billing_email | 201 Workspace |
| GET | /v1/workspaces/{workspace_id} | Bearer · workspace_members:read | Path wrk_* | Workspace · id, name, slug?, type, status, role, billing/plan hints, timestamps |
| PATCH | /v1/workspaces/{workspace_id} | Bearer · workspace_members:write | Partial · name?, slug?, type?, status?, billing_account_id?, billing_customer_ref?, plan?, usage_limit_monthly?, billing_email? | Updated Workspace |
| POST | /v1/workspaces/{workspace_id}/switch | Bearer · workspace_members:read | — | AuthSession scoped to workspace (browser/session clients) |
| GET | /v1/workspaces/{workspace_id}/usage | Bearer · workspace_members:read | — | { "object": "workspace_usage", counts, "recent_api_key_usage": [usage events with paths, ua, authenticated_at, …] } |
Members and invitations
| Method | Path | Auth | Request | Response |
|---|---|---|---|---|
| GET | /v1/workspaces/{workspace_id}/members | Bearer · workspace_members:read | — | { "object": "list", "data": WorkspaceMember[] } |
| POST | /v1/workspaces/{workspace_id}/members | Bearer · workspace_members:write | { "user_id" }; optional email, display_name, role? | 201 WorkspaceMember |
| PATCH | /v1/workspaces/{workspace_id}/members/{user_id} | Bearer · workspace_members:write | Optional role?, status? | WorkspaceMember |
| DELETE | /v1/workspaces/{workspace_id}/members/{user_id} | Bearer · workspace_members:write | — | WorkspaceMember (inactive state) |
| GET | /v1/workspace_members | Bearer · workspace_members:read | — | Members for the caller’s active workspace ({ object, data }) |
| POST | /v1/workspace_members | Bearer · workspace_members:write | Same shape as scoped add-member | 201 WorkspaceMember |
| PATCH | /v1/workspace_members/{user_id} | Bearer · workspace_members:write | WorkspaceMember patch | WorkspaceMember |
| DELETE | /v1/workspace_members/{user_id} | Bearer · workspace_members:write | — | WorkspaceMember |
| GET | /v1/workspaces/{workspace_id}/invitations | Bearer · workspace_members:read | — | { "object": "list", "data": WorkspaceInvitation[] } |
| POST | /v1/workspaces/{workspace_id}/invitations | Bearer · workspace_members:write | { "email" }; optional role?, expires_at? | 201 invitation; invitation_token returned once when created for manual delivery |
| DELETE | /v1/workspaces/{workspace_id}/invitations/{invitation_id} | Bearer · workspace_members:write | — | WorkspaceInvitation revoked |
| POST | /v1/workspace_invitations/accept | Bearer · workspace_members:write | { "invitation_token" } | WorkspaceMember |
WorkspaceMember includes workspace_id, user_id, role, status, created_at.
API keys
| Method | Path | Auth | Request | Response |
|---|---|---|---|---|
| GET | /v1/api_keys | Bearer · api_keys:read | — | { "object": "list", "data": APIKeyInfo[] } (no raw secrets) |
| POST | /v1/api_keys | Bearer · api_keys:write | Optional name?, scopes[], expires_at? (unix) | 201 · includes single-use api_key string sk-* plus metadata |
| GET | /v1/api_keys/{api_key_id} | Bearer · api_keys:read | Path key_* | APIKeyInfo |
| POST | /v1/api_keys/{api_key_id}/activate | Bearer · api_keys:write | — | APIKeyStatus |
| POST | /v1/api_keys/{api_key_id}/deactivate | Bearer · api_keys:write | — | APIKeyStatus |
| DELETE | /v1/api_keys/{api_key_id} | Bearer · api_keys:write | — | APIKeyStatus (deleted) |
Schemas and machine-readable spec
Field-level definitions for payloads (tools, multimodal Input, SSE event types, billing-related workspace fields, etc.) live in the repository OpenAPI document api/openapi/agent-api.v1.yaml. Those schemas track the canonical contract; gateway-only routes documented above include GET /v1/presets, GET /v1/responses, GET /v1/responses/:id/children, and GET /v1/responses/:id/events.