API Overview
Agent One exposes a REST API on /api/v1/* for the web portal and external integrations. The API is contract-first — defined in TypeSpec, compiled to OpenAPI 3.1, with typed clients generated automatically.
Base URL
Section titled “Base URL”http://localhost:4200/api/v1The API runs on the same port as the web portal (configurable via portal.port in config.yaml).
Authentication
Section titled “Authentication”API requests require a Bearer token in the Authorization header:
Authorization: Bearer <token>Tokens are per-member and encode the user’s role. Generate tokens via:
- The
portalbuilt-in tool: “Generate a portal access link for Maria” - The members management API
Role-Based Access
Section titled “Role-Based Access”The token carries the user’s RBAC role. Endpoints enforce permissions:
| Role | Read | Write Config | Manage Hands | Approve Plans | Admin |
|---|---|---|---|---|---|
| owner | yes | yes | yes | yes | yes |
| admin | yes | no | yes | yes | yes |
| member | yes | no | no | no | no |
| guest | no | no | no | no | no |
Error Format
Section titled “Error Format”All errors follow a consistent structure:
{ "error": { "code": "NOT_FOUND", "message": "Hand 'weekly-report' not found", "details": {} }}Error Codes
Section titled “Error Codes”| HTTP Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | Invalid request body or parameters |
| 401 | UNAUTHORIZED | Missing or invalid auth token |
| 403 | FORBIDDEN | Insufficient role permissions |
| 404 | NOT_FOUND | Resource not found |
| 409 | CONFLICT | Resource already exists or state conflict |
| 429 | RATE_LIMITED | Too many requests |
| 500 | INTERNAL_ERROR | Server error |
Pagination
Section titled “Pagination”List endpoints return paginated responses:
{ "data": [...], "pagination": { "page": 1, "page_size": 20, "total": 47, "total_pages": 3 }}Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
page_size | integer | 20 | Items per page (max 100) |
Content Type
Section titled “Content Type”All requests and responses use application/json. The API does not support form-encoded or multipart requests.
WebSocket
Section titled “WebSocket”The chat endpoint uses WebSocket for real-time streaming:
ws://localhost:4200/api/v1/chat/wsClient Messages
Section titled “Client Messages”{ "type": "message", "content": "What's on my schedule today?"}Server Messages
Section titled “Server Messages”{ "type": "chunk", "content": "Let me check..."}{ "type": "done", "content": "You have 3 meetings today: ...", "metadata": { "model": "cheap", "tokens": 142 }}Typed Client
Section titled “Typed Client”A TypeScript client is generated from the OpenAPI spec using openapi-fetch:
import createClient from "openapi-fetch";import type { paths } from "./schema";
const client = createClient<paths>({ baseUrl: "http://localhost:4200/api/v1", headers: { Authorization: `Bearer ${token}` },});
const { data } = await client.GET("/hands");// data is fully typed: { data: Hand[], pagination: Pagination }The generated client is ~3KB at runtime and provides full type safety with zero manual type definitions.
Rate Limiting
Section titled “Rate Limiting”API requests are rate-limited per user (configurable):
safety: rate_limit_per_minute: 20When rate-limited, the API returns 429 Too Many Requests with a Retry-After header.