Skip to content

HTTP API

The brain serves two listeners. This page describes the API one, OPENARITY_API_BIND, which defaults to 127.0.0.1:21120. The other is the inbound gateway, OPENARITY_WEBHOOK_BIND — it authenticates a request by its channel's signing secret rather than by a credential, so nothing here applies to it.

The contract is apps/brain/api/openapi.yaml, hand-written and reviewed as a diff. A test compares the routes the service registers against the paths in it, so adding an endpoint without describing it fails the build. The CLI's client is generated from the same file.

Authenticating

Authorization: Bearer <token> on everything except the probes and /auth/config. The token is an OIDC access token, or the shared development token when OPENARITY_DEV_TOKEN is set. The scheme is matched case-insensitively.

GET /auth/config is readable before you can authenticate, and says how to.

Endpoints

Every operation in openapi.yaml, in its order, with its summary. The spec is the contract — the CLI's client is generated from it — so it holds every request and response field this table leaves out.

Method Path What it does
GET /healthz Liveness
GET /readyz Readiness
GET /auth/config How to obtain a token
GET /whoami The authenticated caller
GET /teams List teams
POST /teams Create a team
GET /teams/{id} Read one team
DELETE /teams/{id} Delete a team (super admin)
POST /teams/{id}/restore Restore a deleted team (super admin)
GET /teams/{id}/permissions What the caller may do in a team
GET /teams/{id}/members List a team's members
POST /teams/{id}/members Add a member
DELETE /teams/{id}/members/{userID} Remove a member
GET /teams/{id}/channels List a team's channels
POST /teams/{id}/channels Connect a channel
DELETE /teams/{id}/channels/{channelID} Disconnect a channel
GET /teams/{id}/channels/{channelID}/senders/pending List senders waiting to be approved
GET /teams/{id}/channels/{channelID}/senders List approved senders
POST /teams/{id}/channels/{channelID}/senders Approve a sender
DELETE /teams/{id}/channels/{channelID}/senders Remove a sender, or dismiss a pending one
GET /teams/{id}/channels/{channelID}/sessions List a channel's conversations
GET /teams/{id}/sessions List every conversation in a team
GET /teams/{id}/sessions/{sessionID} Read a conversation's details
GET /teams/{id}/sessions/{sessionID}/messages Read a conversation
GET /teams/{id}/sessions/{sessionID}/runs List the runs in a conversation
GET /teams/{id}/sessions/{sessionID}/attachments List the files in a conversation
GET /teams/{id}/sessions/{sessionID}/attachments/{attachmentID} Download a file
GET /teams/{id}/skills List a team's skills
POST /teams/{id}/skills Upload a skill
GET /teams/{id}/skills/{skillID} Read a skill
PUT /teams/{id}/skills/{skillID} Replace a skill
DELETE /teams/{id}/skills/{skillID} Delete a skill
GET /teams/{id}/skills/{skillID}/files/{path} Download one of a skill's files
POST /teams/{id}/skills/import Import a skill from GitHub or a zip on the web
POST /teams/{id}/skills/{skillID}/sync Fetch an imported skill's source again
GET /teams/{id}/agents List a team's agents
POST /teams/{id}/agents Define an agent
GET /teams/{id}/agents/{agentID} Read an agent, with its grants
PUT /teams/{id}/agents/{agentID} Replace an agent and its grants
DELETE /teams/{id}/agents/{agentID} Delete an agent
GET /teams/{id}/loops List the loops an agent may use
POST /teams/{id}/runs Start a run of an agent, or ask the team's hub
GET /teams/{id}/runs/{runID} Read a run
GET /teams/{id}/runs/{runID}/events Follow what a run does
POST /teams/{id}/runs/{runID}/cancel Cancel a run
GET /teams/{id}/settings Read a team's settings
PUT /teams/{id}/settings Change a team's settings
GET /teams/{id}/mcp-servers List a team's MCP servers
POST /teams/{id}/mcp-servers Register an MCP server
GET /teams/{id}/mcp-servers/{serverID} Read an MCP server
PUT /teams/{id}/mcp-servers/{serverID} Replace an MCP server
DELETE /teams/{id}/mcp-servers/{serverID} Delete an MCP server
GET /teams/{id}/mcp-servers/{serverID}/tools List the tools a server offers
POST /teams/{id}/mcp-servers/{serverID}/discover Ask a server which tools it offers, and store them
GET /teams/{id}/graph The team's graph
GET /users Find a user

Two things that surprise people

Errors are text/plain, not JSON. Successful responses are JSON; failures carry a short sentence and nothing machine-readable. Branch on the status code, never on the body.

404 does not mean the thing does not exist. A team you cannot see answers 404 rather than 403, so a caller cannot map what exists by probing.

Paging

Every list takes limit and cursor as query parameters and answers with an envelope, never a bare array:

{ "items": [], "next_cursor": "…" }

next_cursor is absent on the last page, and its absence is the only end-of-collection signal — there is no separate flag that could disagree with it. It is opaque and not constructible by a client; an altered one is a 400 rather than a silent restart.

limit defaults to 50 and is clamped at 100, so asking for a thousand gets you a hundred rather than an error.