Skip to content

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.

http://localhost:4200/api/v1

The API runs on the same port as the web portal (configurable via portal.port in config.yaml).

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 portal built-in tool: “Generate a portal access link for Maria”
  • The members management API

The token carries the user’s RBAC role. Endpoints enforce permissions:

RoleReadWrite ConfigManage HandsApprove PlansAdmin
owneryesyesyesyesyes
adminyesnoyesyesyes
memberyesnononono
guestnonononono

All errors follow a consistent structure:

{
"error": {
"code": "NOT_FOUND",
"message": "Hand 'weekly-report' not found",
"details": {}
}
}
HTTP StatusCodeDescription
400BAD_REQUESTInvalid request body or parameters
401UNAUTHORIZEDMissing or invalid auth token
403FORBIDDENInsufficient role permissions
404NOT_FOUNDResource not found
409CONFLICTResource already exists or state conflict
429RATE_LIMITEDToo many requests
500INTERNAL_ERRORServer error

List endpoints return paginated responses:

{
"data": [...],
"pagination": {
"page": 1,
"page_size": 20,
"total": 47,
"total_pages": 3
}
}

Query parameters:

ParameterTypeDefaultDescription
pageinteger1Page number
page_sizeinteger20Items per page (max 100)

All requests and responses use application/json. The API does not support form-encoded or multipart requests.

The chat endpoint uses WebSocket for real-time streaming:

ws://localhost:4200/api/v1/chat/ws
{
"type": "message",
"content": "What's on my schedule today?"
}
{
"type": "chunk",
"content": "Let me check..."
}
{
"type": "done",
"content": "You have 3 meetings today: ...",
"metadata": {
"model": "cheap",
"tokens": 142
}
}

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.

API requests are rate-limited per user (configurable):

safety:
rate_limit_per_minute: 20

When rate-limited, the API returns 429 Too Many Requests with a Retry-After header.