API reference

One /v1 endpoint for every provider. Point your OpenAI client at a new base URL and key, bring your own provider keys, and pay 0% markup on tokens.

Every endpoint, generated from the same OpenAPI description the API publishes at /openapi.json, so it cannot list an endpoint the API lacks or miss one it has. Load that file into Postman, Insomnia or any OpenAPI code generator to build a client.

Earlier names

The product was first called AgentsRouter, and its own names carried it. They still work and always will: requests may send X-AgentsRouter-Agent or X-AgentRouter-Metadata instead of X-Autonomous-Relay-Agent and X-Autonomous-Relay-Metadata, and declare a server tool as agentrouter:datetime instead of autonomousrelay:datetime. Responses and webhooks carry every x-autonomous-relay-* header under its x-agentsrouter-* name as well, with the same value, so a receiver checking x-agentsrouter-signature keeps working unchanged.

265 endpoints in 30 groups.

Inference

Model requests.

Create a chat completion

POST /v1/chat/completions

OpenAI-compatible. Routes to an endpoint your guardrails and preferences allow, dispatches on your own provider credential, and fails over to the next candidate before the first byte ships -- walking models[] to exhaustion.

Free models without a key. A free model (GET /v1/models?free=true: every price zero) can be used before you add any provider key: where the platform holds a free-model key for its provider, an organisation with no key of its own for that model is served on it, at $0, with no hold. At most 50 such requests per organisation per UTC day (the operator may set another figure), across every free model and key; past that the attempt is not sent and the request is a 429 with metadata.reason: free_model_daily_limit. Your own key has no such limit, and once you add one for the provider it is used instead. The provider's own free-tier limits still apply, and its errors pass through. Every model with a price is served on your own key only.

With stream: true the response is server-sent events: one data: line per ChatCompletionChunk, SSE comments (lines starting with :) as keepalives, a final chunk carrying usage, then data: [DONE].

Authentication

Inference key (ar-v1-…) · Dashboard session

Parameters

X-Autonomous-Relay-Metadata enum · header

enabled adds agentrouter_metadata: on the final chunk when streaming, and beside error on an error once routing has begun. X-Metadata is accepted too.

X-Session-Id string · header

A sticky-routing key, used when the body has no session_id. The legacy X-AgentRouter-Metadata is accepted too.

X-Anthropic-Beta string · header

Forwarded to Anthropic as anthropic-beta.

HTTP-Referer string · header

Your app's URL, for attribution. The app listing keeps its path; each request row keeps only the scheme and host.

X-Autonomous-Relay-Agent string · header

The agent this request is for: 1 to 64 letters, digits, ., _, : or -, starting with a letter or digit. Recorded on the request, and bound by that agent's budgets (PUT /v1/agents/{agent}/budgets/{interval}) across every key it uses. A malformed value is a 400 before anything is spent. The legacy name X-AgentsRouter-Agent is accepted too.

X-Title string · header

Your app's name. X-App-Title and X-OpenRouter-Title are accepted too.

X-App-Visibility enum · header

hidden keeps the app out of public listings. Read on the app's first request only.

X-App-Categories string · header

Up to two comma-separated categories.

X-Cache enum · header

bypass answers from the provider and stores nothing, on a key with the response cache on.

Request body

model string

A catalog id, author/slug, optionally with a variant (:free, :nitro, :floor, :exacto), or a ~author/family alias, which resolves to the author/family-latest row when the catalog publishes one and otherwise resolves to nothing -- it never picks a model for you by guessing at the name. An id listed in /v1/models always means that listing: some free tiers are catalog rows of their own, named author/slug:free, and a suffix is only split off the id when no such row can serve the request. It always applies as a variant either way. Or @preset/<slug> (@<version> to pin one): your organisation's preset, whose settings fill in whatever this request does not set.

models string[]

A fallback chain, tried in order. At most three are attempted.

messages ChatMessage[]
prompt string

Legacy: sent as a single user message when messages is absent.

stream boolean
Default: false
max_tokens integer
max_completion_tokens integer
temperature number
top_p number
top_k integer
stop string | string[]
seed integer
tools object[]

Client functions, and server tools the gateway runs itself: {"type": "autonomousrelay:datetime", "parameters": {"timezone": "America/Toronto"}}, {"type": "autonomousrelay:experimental__search_models"}, or a registered MCP server's tools: {"type": "autonomousrelay:mcp", "server": "<name>", "allowed_tools": [...]} (§17.7; see POST /v1/mcp-servers), offered to the model as mcp__<server>__<tool> and called by the gateway on the server's credential. An installed marketplace plugin's tools: {"type": "autonomousrelay:plugin", "plugin": "<slug>", "allowed_tools": [...]} (see POST /v1/plugins/{id}/install), offered as plugin__<slug>__<tool> and called on the install's own credential -- {"type": "autonomousrelay:plugin", "plugin": "notion"} offers plugin__notion__search_pages and plugin__notion__read_page; a plugin not installed is a 404, one turned off a 403, one that cannot list its tools a 502. Every connection at once: {"type": "autonomousrelay:connected", "include": [...], "exclude": [...], "writes": [...]} offers the READ tools of every installed plugin and MCP server (at most 10 connections and 128 functions per request), plus only the write tools named exactly in writes -- which need a run_id whose agent has an approval policy, and always wait for a person. A connection that does not work (not connected, expired, turned off, unreachable) is skipped, not an error: the answer comes from the rest, the X-Autonomous-Relay-Connected-Tools response header and agentrouter_metadata.connected_tools say what was used and skipped, and the organisation's alert channels (or its owners, by email) are told once a day per connection. GET /v1/tools/available shows what it would draw on. With "stream": true the loop still runs: its progress arrives as SSE comment lines (: autonomousrelay {"type": "tool.called", ...}, which SDKs ignore), then the answer as ordinary chunks. Their use is reported in usage.server_tool_use. See GET /v1/tools for the catalogue and which are available.

max_tool_calls integer

The most server-tool executions this request may make.

Default: 30
tool_context object

Context for connector tools: a plugin tool whose connector manifest declares a key receives that key's value as _context. A hosted agent's run started by a connector event sets it from the event. Never sent to the model provider.

tool_choice any

none, auto, required, or {"type": "function", "function": {"name": …}}.

service_tier enum

OpenAI's processing tier. flex (Batch rates; OpenAI may refuse it with a 429 when busy, uncharged) and priority / fast (Fast mode, a per-token premium) are sent only to an OpenAI endpoint with a published price for that tier; anywhere else the request is served at standard. Billing follows the tier the provider reports serving at -- a Fast request OpenAI serves at default is billed at default. auto, default and scale are accepted and not forwarded.

One of: auto, default, flex, priority, fast, scale
response_format object

{"type": "json_object"} or {"type": "json_schema", "json_schema": {"name", "schema", "strict"}}. Passed as is on OpenAI-protocol providers; on Gemini it becomes JSON mode with the schema; on Anthropic a json_schema becomes structured outputs, with the bounds Anthropic does not enforce (minimum, maxLength, maxItems, …) left unchecked and additionalProperties: false assumed where unset. A schema Anthropic cannot express (additionalProperties other than false, a recursive or external $ref) is sent there without a format; json_object has no Anthropic counterpart. The response-healing plugin repairs a reply that is still not JSON.

plugins object[]

[{"id": "response-healing"}] repairs a reply that should have been JSON (response_format json_object or json_schema): markdown fences, surrounding text, trailing commas, comments, bare keys, unclosed brackets. The repair is used only if it parses; a reply that already is JSON, or that max_tokens cut short (finish_reason: "length"), is returned as written. Not on streams. When it changed the answer, agentrouter_metadata.pipeline includes response_healing.

{"id": "context-compression"} fits a conversation to the serving endpoint's context window by removing whole messages from the middle out: system and developer messages, the first message and the last always stay, and a tool call goes with its results. The window left for the prompt is the context less max_tokens (a quarter of the window when unset). On by default for endpoints of 8,192 tokens or less; {"id": "context-compression", "enabled": false} turns it off. With it on, Anthropic's 1,000-message limit is met the same way. Never applied to an image-output model. A failover to a larger endpoint sends the conversation whole. When it removed messages, pipeline includes context_compression.

{"id": "file-parser", "pdf": {"engine": "pdf-text" | "native"}} decides how a PDF file part (file_data as a base64 data URL, or a file_id) reaches the model. native sends the file; pdf-text sends its text layer, read on our side, as a text part headed by the filename, page by page (at most 200 pages and 1,000,000 characters per request). Unset, each endpoint decides: the file where the model reads files, its text everywhere else, so PDFs work on any model. mistral-ocr is refused: OCR would send the document to a third party, so a scanned PDF has no text here and should go to a model that reads PDFs. A file that is not a PDF, or is password-protected, is a 400 when an endpoint needs its text. When the model was given text, the answer carries it as message.annotations (on a stream, on the final chunk's delta.annotations) [{"type": "file", "file": {"hash", "name", "content": [{"type": "text", "text"}]}}]; send that assistant message back with the conversation and the annotation is used instead of reading the file again. pipeline includes file_parser. Nothing is stored.

{"id": "auto-router", "cost_tier": "low" | "medium" | "high" | "xhigh" | "max", "allowed_models": [...], "excluded_models": [...]} configures "model": "autonomousrelay/auto": a paid model you can route to that fits the request (text out, image input when an image is sent, a window that holds it), from the tier by output price per million tokens (low to $2, the default; medium to $10; high to $30; xhigh to $60; max above), the most expensive first, then the newest; two fallbacks from the same tier; the nearest cheaper tier when the one asked for has nothing, then the nearest dearer one. Patterns take * for any run of characters. The prompt is not read and nothing is kept, so the same request picks the same model.

{"id": "moderation", "input": true, "output": false} checks the input (default) and, with output: true, the answer, using OpenAI's omni-moderation-latest on the organisation's own OpenAI BYOK key (a 400 without one). The input is checked before any provider sees it and before anything is held; a flag is a 403 with error_type moderation_flagged and metadata {reasons, flagged_input, provider_name, model_slug}. A flagged answer is withheld, and the request is billed because the model ran. Output moderation is refused on streams. If moderation cannot run, the request is refused (503), never sent unchecked. pipeline includes moderation.

{"id": "web", "engine": "native", "max_uses": 3, "allowed_domains": [...] | "excluded_domains": [...]} lets the model search the web with its provider's own search: Anthropic's web search tool on Claude models, Google Search grounding on Gemini 3 models, and OpenAI's and xAI's web_search tool (the request is run on their Responses API) on GPT-5 and later and Grok 4 models. Gemini 2.5 is not served: it is priced per grounded prompt. Only those endpoints are candidates; with none, a 404. max_uses (1-20) applies on Claude; the domain lists on Claude, OpenAI and xAI (at most 5 on xAI), so a request with domains never goes to Gemini. Each search is metered as a web_search line at the provider's published price per 1,000: $10 Anthropic, $14 Gemini (before Google's free monthly allowance), $10 OpenAI, $5 xAI. The pages the search cited come back as url_citation annotations on the message (on a stream, in delta.annotations as they arrive), with start_index/end_index where the provider gives character offsets. Outside engines (Exa and the like) are refused: they would be a new recipient of your queries. Other plugin ids are ignored.

reasoning object

{ effort: minimal|low|medium|high|xhigh|max } or { max_tokens: n }. On Anthropic's current models (Fable, Opus 4.6 and later, Sonnet 4.6 and later) this becomes adaptive thinking with effort as the depth (minimal is low; xhigh is high on the 4.6 models); a max_tokens budget turns thinking on at the model's default depth, since those models size their thinking inside the request's own max_tokens. On older Claude models it is extended thinking with that budget (floor 1024). Either way the thinking text is returned as message.reasoning (or delta.reasoning when streaming). temperature, top_p and top_k are dropped alongside thinking, and on Fable, Opus 4.7 and later and Sonnet 5 always, because Anthropic refuses them there.

provider ProviderPreferences

Routing preferences (§6.2). Preferences reorder; constraints reject. Guardrails outrank everything here.

session_id string

Keeps a conversation on one endpoint so its prompt cache stays warm (§6.6).

prompt_cache_key string

Used as the sticky-routing key when there is no session_id.

routing_stability enum

Defaults to sticky when the request has a pin key, adaptive otherwise (§17.2).

One of: adaptive, sticky, pinned

Responses

200 The completion: JSON, or an event stream when `stream` is true.
id stringrequired

gen-…. Pass it to GET /v1/generation for the full record.

object anyrequired
created integerrequired

Unix seconds.

model stringrequired

The model that served, which differs from the one requested when a fallback fired.

provider string
choices object[]required
usage Usagerequired
agentrouter_metadata RouterMetadata

Why this model, this provider, this price (§7.5). Present only when X-Autonomous-Relay-Metadata: enabled was sent.

400 `messages is required`, or `model is required`.
401 No key; an invalid, disabled or expired key; or an invalid session.
402 Not enough credit for the worst-case hold. Once a balance is negative every request is refused, free models included.
403 A budget is exhausted (`budget_exceeded`; the message names the level and the limit), a content filter blocked the prompt (`guardrail_blocked`; the message names the labels, never the matched text), or the key is a management key -- in which case the body reports `code: 401` and `invalid_credentials`. With a `run_id`, also `run_budget_exceeded` (§17.1). That one type covers every way a run refuses a turn -- its cost ceiling, `max_turns`, `max_tool_calls`, a `model_policy` that excludes the model, and a run that is no longer open. §17.1 specifies it for "the run's budget and caps", and the **message** names which one bound; a `model_policy` or closed-run refusal therefore carries a type that reads narrower than it is.
404 No endpoint can serve the model under your constraints (`no_endpoints_available`). `metadata.unsatisfied_filters` names the filters that emptied the set. Also `not_found` when `run_id` names a run this organization does not have -- a different problem from a run that ran out, and fixed differently.
429 Rate limited (`rate_limited`): by the account's quota (`X-RateLimit-*`), or by the key's own `rpm_limit` or `tpm_limit` (`X-RateLimit-*-Requests`, `X-RateLimit-*-Tokens`; the message names which). `Retry-After` says when to try again.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 Every candidate failed upstream (`provider_error`). `metadata.provider_code` and `metadata.provider_name` describe the last.
503 `service_unavailable`: moderation could not run, so the request was not sent (or, after the model ran, its answer is withheld and billed).
529 Every candidate was overloaded upstream. The body reports `code: 502`.

Create a message (Anthropic's Messages API)

POST /v1/messages

Anthropic-compatible: the Anthropic SDKs work with their base URL set to this host. The request is translated to a chat completion and served by the same pipeline, so every gate, routing rule, preset, server tool and file applies, and any model answers it, not only Claude. The answer comes back as a message, or with stream: true as Anthropic's events (message_start, content_block_*, message_delta, message_stop, ping).

The key goes in x-api-key (as the Anthropic SDK sends it) or Authorization: Bearer. anthropic-version is accepted and ignored; anthropic-beta is forwarded to Anthropic. A Claude name such as claude-opus-4-8 or claude-opus-4-8-20260101 means anthropic/claude-opus-4.8; any other model is named by its catalogue id. models (or fallbacks: [{"model": ...}], at most three), provider, session_id and run_id work as on chat completions.

Not taken: Anthropic's own server tools (declare autonomousrelay:* tools instead -- autonomousrelay:plugin, autonomousrelay:mcp and autonomousrelay:connected included, as on chat completions), images inside a tool_result, a document by URL, and cache_control (ignored). Thinking blocks sent back are dropped; tool-use arguments stream as one input_json_delta per call, once the model has finished.

Authentication

Inference key (ar-v1-…) · Dashboard session

Parameters

x-api-key string · header

An inference key, as the Anthropic SDK sends it. Used when there is no Authorization header.

anthropic-version string · header

Accepted and ignored.

anthropic-beta string · header

Forwarded to Anthropic.

X-Autonomous-Relay-Agent string · header

The agent this request is for, as on chat completions.

Request body

model stringrequired

A catalogue id, or a Claude name (claude-opus-4-8).

max_tokens integerrequired
messages object[]required
system string | object[]
stream boolean
temperature number
top_p number
top_k integer
stop_sequences string[]
tools object[]
tool_choice object
thinking object
metadata object
fallbacks object[]

Models tried in order after model.

Responses

200 A message, or an event stream when `stream` is true.
id string

The request id; GET /v1/generation?id= reads it.

type enum
One of: message
role enum
One of: assistant
model string

The model that served.

content object[]
stop_reason enum
One of: end_turn, max_tokens, stop_sequence, tool_use, pause_turn, refusal
stop_sequence string | null
usage object
400 In Anthropic's envelope (`{"type": "error", "error": {"type": "invalid_request_error", ...}}`), like every refusal here: a field that cannot be translated, named.
401 `authentication_error`.
402 `billing_error`: not enough credit for the worst-case hold.
403 `permission_error`: a budget, a content filter, or a management key. `error.metadata.error_type` carries our own type.
404 `not_found_error`: no such model, or no endpoint under your constraints.
429 `rate_limit_error`.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 `api_error`: every candidate failed upstream.
529 `overloaded_error`.

Create a response (OpenAI's Responses API)

POST /v1/responses

OpenAI-compatible Responses API. The request is translated to a chat completion and served by the same pipeline, so every gate, routing rule, preset, server tool and file applies, and any model answers it. The answer is a response; with stream: true, the Responses events (response.created, response.output_item.*, response.output_text.delta, response.function_call_arguments.*, response.completed or response.incomplete), each with a sequence_number.

Stateless. Nothing is stored, so store: true, previous_response_id, conversation and background are refused with a 400 rather than ignored; send the whole conversation as input. store: false is accepted.

Input items: messages (input_text, input_image by URL or file_id, input_file by file_data or file_id), function_call and function_call_output; reasoning items are dropped. Tools: function, and our autonomousrelay:* declarations -- server tools, autonomousrelay:plugin, autonomousrelay:mcp and autonomousrelay:connected, as on chat completions; OpenAI's built-in tools are refused. text.format (json_schema, json_object), reasoning.effort, max_output_tokens, tool_choice and parallel_tool_calls map to their chat equivalents; models and provider work as on chat completions. Function-call arguments stream as one delta per call, once the model has finished.

Authentication

Inference key (ar-v1-…) · Dashboard session

Parameters

X-Autonomous-Relay-Agent string · header

The agent this request is for, as on chat completions.

Request body

model string

A catalogue id.

input string | object[]required
instructions string

Sent as a system message ahead of the input.

max_output_tokens integer
stream boolean
temperature number
top_p number
user string
tools object[]
tool_choice enum | object
parallel_tool_calls boolean
text object
reasoning object
store boolean

Only false (or absent): nothing is stored.

models string[]
provider object

Routing preferences, as on chat completions.

Responses

200 A response, or an event stream when `stream` is true.
id string

The request id; GET /v1/generation?id= reads it.

object enum
One of: response
created_at integer
status enum
One of: completed, incomplete
incomplete_details object | null
model string

The model that served.

output object[]
usage object
400 A field that needs a stored response (`store: true`, `previous_response_id`, `conversation`, `background`), or one that cannot be translated, named.
401 No key; an invalid, disabled or expired key; or an invalid session.
402 Not enough credit for the worst-case hold.
403 A budget is exhausted, a content filter blocked the input, or the key is a management key.
404 No such model, or no endpoint under your constraints.
429 Rate limited (`rate_limited`): by the account's quota (`X-RateLimit-*`), or by the key's own `rpm_limit` or `tpm_limit` (`X-RateLimit-*-Requests`, `X-RateLimit-*-Tokens`; the message names which). `Retry-After` says when to try again.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 Every candidate failed upstream.

Create a text completion (legacy)

POST /v1/completions

OpenAI's legacy text completion, for clients that still call it. The prompt is sent as one user message and served by the chat pipeline -- every gate, routing rule and preset applies -- and the answer's text comes back as choices[0].text; with stream: true, as text_completion chunks ending in data: [DONE].

One prompt per request: a list of more than one is refused, since each would be its own routed and billed request. The catalogue's models are chat models, so what only a true completion model can honour is refused rather than dropped: suffix, echo, best_of and n above 1, logprobs (chat completions return them), and token-id prompts.

Authentication

Inference key (ar-v1-…) · Dashboard session

Parameters

X-Autonomous-Relay-Agent string · header

The agent this request is for, as on chat completions.

Request body

model string

A catalogue id.

prompt string | string[]required
max_tokens integer
stream boolean
temperature number
top_p number
stop string | string[]
seed integer
frequency_penalty number
presence_penalty number
user string
models string[]
provider object

Routing preferences, as on chat completions.

Responses

200 The completion, or `text_completion` chunks when `stream` is true.
id string

The request id; GET /v1/generation?id= reads it.

object enum
One of: text_completion
created integer
model string

The model that served.

choices object[]
usage object

As on chat completions.

400 More than one prompt, a token-id prompt, or `suffix`, `echo`, `best_of`, `n` above 1 or `logprobs`, named.
401 No key; an invalid, disabled or expired key; or an invalid session.
402 Not enough credit for the worst-case hold.
403 A budget is exhausted, a content filter blocked the prompt, or the key is a management key.
404 No such model, or no endpoint under your constraints.
429 Rate limited (`rate_limited`): by the account's quota (`X-RateLimit-*`), or by the key's own `rpm_limit` or `tpm_limit` (`X-RateLimit-*-Requests`, `X-RateLimit-*-Tokens`; the message names which). `Retry-After` says when to try again.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 Every candidate failed upstream.

Create embeddings

POST /v1/embeddings

OpenAI-compatible. Routed like a chat completion -- model, an optional models[] fallback list and provider preferences -- through the same gates in the same order: auth, rate limit, every budget, the content filters (a redacted input is what is embedded), your guardrails and privacy policy, a hold, and failover before anything is returned. Billed on the provider's prompt-token count.

Only models whose catalogue modality outputs embeddings are candidates; naming a chat model is a 400 saying so. input is a string or up to 2,048 strings; token arrays are refused, since the router cannot know which tokeniser the endpoint it picks will read them with.

Authentication

Inference key (ar-v1-…) · Dashboard session

Parameters

X-Autonomous-Relay-Agent string · header

The agent this request is for; bound by that agent's budgets, as on chat completions.

HTTP-Referer string · header

Your app's URL, for attribution.

X-Title string · header

Your app's name.

Request body

model string

An embedding model id from /v1/models.

models string[]

Fallbacks, tried in order after model.

input string | string[]required
encoding_format enum

base64 returns each vector as little-endian float32s, base64-encoded.

One of: float, base64
dimensions integer

For models that can shorten their output; others refuse it upstream.

provider object

Routing preferences, as on chat completions.

Responses

200 One vector per input, in input order.
object enum
One of: embedding
index integer
embedding number[] | string
400 A malformed `input`, `encoding_format`, `dimensions` or `provider`; `model is required`; or the model is not an embedding model.
401 No key; an invalid, disabled or expired key; or an invalid session.
402 Not enough credit for the worst-case hold.
403 A budget is exhausted (`budget_exceeded`), a content filter blocked the input (`guardrail_blocked`), or the key is a management key.
404 No model by that name, or no endpoint can serve it under your constraints (`no_endpoints_available`).
429 Rate limited (`rate_limited`): by the account's quota (`X-RateLimit-*`), or by the key's own `rpm_limit` or `tpm_limit` (`X-RateLimit-*-Requests`, `X-RateLimit-*-Tokens`; the message names which). `Retry-After` says when to try again.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 Every candidate failed upstream (`provider_error`).
524 Every candidate timed out.

Text to speech

POST /v1/audio/speech

OpenAI's request; the answer is the audio itself, in response_format (mp3 by default). Routed like embeddings -- model, models and provider as on chat completions, the same budgets, content filters (a redacted input is what is spoken) and guardrails. Only text-to-speech models with a published price are candidates, and the request is billed per character of input, at that price. voice is passed as given: each model has its own voices (af_bella on Kokoro, Ashley on Inworld, a free-text description on the VoiceDesign models), and its default when omitted.

The body is audio, so the request id travels in the x-autonomous-relay-generation-id header; GET /v1/generation?id= reads its cost.

Authentication

Inference key (ar-v1-…) · Dashboard session

Parameters

X-Autonomous-Relay-Agent string · header

The agent this request is for, as on chat completions.

Request body

model string

A text-to-speech model id from /v1/models.

models string[]

Fallbacks, tried in order after model.

input stringrequired
voice string

The model's own voice name or, on a VoiceDesign model, a description.

response_format enum
One of: mp3, opus, flac, wav, pcm
speed number
provider object

Routing preferences, as on chat completions.

Responses

200 The audio.
400 A malformed `input`, `voice`, `response_format` or `speed`; `model is required`; or the model is not a text-to-speech model.
401 No key; an invalid, disabled or expired key; or an invalid session.
402 Not enough credit for the worst-case hold.
403 A budget is exhausted, a content filter blocked the input, or the key is a management key.
404 No model by that name, or no endpoint can serve it -- `published_price` when it has no price we can compute.
429 Rate limited (`rate_limited`): by the account's quota (`X-RateLimit-*`), or by the key's own `rpm_limit` or `tpm_limit` (`X-RateLimit-*-Requests`, `X-RateLimit-*-Tokens`; the message names which). `Retry-After` says when to try again.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 Every candidate failed upstream.

Speech to text

POST /v1/audio/transcriptions

OpenAI's multipart request: file (flac, mp3, mp4, mpeg, mpga, m4a, ogg, opus, wav or webm, at most 25 MB) and model, with optional language (ISO-639-1), prompt and temperature. Routed like embeddings, through the same budgets and guardrails; the content filters read prompt. Only speech-to-text models with a published price are candidates, and the request is billed per second of audio, as the provider measured it.

response_format: json ({text, usage}), verbose_json (adds language, duration, segments, words), or text (the transcript as plain text). usage is {type: "duration", seconds, cost, ...}. The request id is also in the x-autonomous-relay-generation-id header.

Authentication

Inference key (ar-v1-…) · Dashboard session

Parameters

X-Autonomous-Relay-Agent string · header

The agent this request is for, as on chat completions.

Responses

200 The transcript.
text string
usage object
400 No file, a file that is not audio, a malformed field, or the model is not a speech-to-text model.
401 No key; an invalid, disabled or expired key; or an invalid session.
402 Not enough credit for the worst-case hold.
403 A budget is exhausted, a content filter blocked the prompt, or the key is a management key.
404 No model by that name, or no endpoint can serve it.
413 The file is over 25 MB.
429 Rate limited (`rate_limited`): by the account's quota (`X-RateLimit-*`), or by the key's own `rpm_limit` or `tpm_limit` (`X-RateLimit-*-Requests`, `X-RateLimit-*-Tokens`; the message names which). `Retry-After` says when to try again.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 Every candidate failed upstream, or answered without the duration it is billed on.

Generate images

POST /v1/images/generations

OpenAI's request and response, routed like embeddings: model, models and provider as on chat completions, through the same budgets, content filters (a redacted prompt is what is drawn) and guardrails. Only text-to-image models with a published output price are candidates; a model whose price cannot be computed from the request (per token, tiered by megapixel) is not served, and the 404 names published_price.

Billed at that price, per image or per megapixel of size ((width/1024) x (height/1024)), for the images actually returned; GET /v1/models/{author}/{slug}/endpoints shows each endpoint's image_output price and its image_output_unit. Images come back as b64_json only: nothing is hosted, so there is no URL to give. Up to 4 images; each side of size from 256 to 2048 (models may accept less).

Authentication

Inference key (ar-v1-…) · Dashboard session

Parameters

X-Autonomous-Relay-Agent string · header

The agent this request is for, as on chat completions.

Request body

model string

A text-to-image model id from /v1/models.

models string[]

Fallbacks, tried in order after model.

prompt stringrequired
n integer
size string

WIDTHxHEIGHT or auto (1024x1024).

response_format enum
One of: b64_json
provider object

Routing preferences, as on chat completions.

Responses

200 The images.
b64_json string
revised_prompt string
400 A malformed `prompt`, `n`, `size` or `response_format`; `model is required`; or the model is not a text-to-image model.
401 No key; an invalid, disabled or expired key; or an invalid session.
402 Not enough credit for the worst-case hold.
403 A budget is exhausted, a content filter blocked the prompt, or the key is a management key.
404 No model by that name, or no endpoint can serve it -- `published_price` when it has no output price we can compute.
429 Rate limited (`rate_limited`): by the account's quota (`X-RateLimit-*`), or by the key's own `rpm_limit` or `tpm_limit` (`X-RateLimit-*-Requests`, `X-RateLimit-*-Tokens`; the message names which). `Retry-After` says when to try again.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 Every candidate failed upstream.
524 Every candidate timed out.

Rerank documents against a query

POST /v1/rerank

Each document's relevance to the query, most relevant first -- the request and response shape Cohere and Jina share. Routed and billed like embeddings: model, models and provider as on chat completions, the same budgets, content filters (a redacted query or document is what is scored) and guardrails, and the provider's input-token count as the bill. Only reranker models are candidates; another kind is a 400 that says so.

Served today by DeepInfra's Qwen3 rerankers. Up to 1,000 documents, each a string or {"text": ...}. instruction steers the task on models that take one.

Authentication

Inference key (ar-v1-…) · Dashboard session

Parameters

X-Autonomous-Relay-Agent string · header

The agent this request is for, as on chat completions.

Request body

model string

A reranker model id from /v1/models.

models string[]

Fallbacks, tried in order after model.

query stringrequired
documents string | object[]required
top_n integer

How many results to return; all by default.

return_documents boolean

Include each result's text.

instruction string

Steers the reranking task, on models that take one.

provider object

Routing preferences, as on chat completions.

Responses

200 The results, most relevant first.
id string

The request id; GET /v1/generation?id= reads it.

object enum
One of: list
model string

The model that served.

provider string

The provider that served.

results object[]
usage object
400 A malformed `query`, `documents`, `top_n`, `return_documents` or `instruction`; `model is required`; or the model is not a reranker.
401 No key; an invalid, disabled or expired key; or an invalid session.
402 Not enough credit for the worst-case hold.
403 A budget is exhausted, a content filter blocked the input, or the key is a management key.
404 No model by that name, or no endpoint can serve it under your constraints.
429 Rate limited (`rate_limited`): by the account's quota (`X-RateLimit-*`), or by the key's own `rpm_limit` or `tpm_limit` (`X-RateLimit-*-Requests`, `X-RateLimit-*-Tokens`; the message names which). `Retry-After` says when to try again.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 Every candidate failed upstream.
524 Every candidate timed out.

Preview how a request would route

POST /v1/route/preview

Answers "why would I get that model, from that provider?" before the money is spent. Router metadata answers it afterwards; this runs the same gates, the same filters and the same ranking against a request that is never dispatched.

It mirrors POST /v1/chat/completions gate for gate and in order: the same auth, the same budget checks in the same precedence, the same content filter applied before routing, the same per-model candidate load, filter and rank, the same fallback depth, and the same sticky hoist.

It has no side effects. No ledger hold, no run reservation, no request row, no sticky pin written, no provider contacted. Rate limited in its own bucket, so debugging a routing problem never eats the quota needed to fix it.

Read ordering before trusting cascade as a prediction: the default ranking is a weighted random draw, and sampled means a real request draws again.

Authentication

Inference key (ar-v1-…) · Dashboard session

Parameters

X-Session-Id string · header

A sticky-routing key, used when the body has no session_id.

Request body

model string

The model to route, exactly as /v1/chat/completions takes it -- :free, :nitro, :floor, :exacto suffixes and ~author/family-latest aliases included.

models string[]

A fallback chain, previewed in order. Walked to the same depth a real request would walk it.

provider ProviderPreferences

Routing preferences (§6.2). Preferences reorder; constraints reject. Guardrails outrank everything here.

tools object[]

Only the COUNT matters here: a non-empty list makes tools a required parameter, which is a hard filter under require_parameters.

messages ChatMessage[]

Optional. Supplied only so content filters and the sticky prefix hash can be evaluated; nothing is sent to a provider. Omit them and content_filter comes back null rather than a fabricated allow.

session_id string

Sticky-routing key, as on the chat route.

prompt_cache_key string
routing_stability enum
One of: adaptive, sticky, pinned
routing_seed string

Seeds the weighted draw so a preview is reproducible and quotable in a bug report. Named routing_seed, not seed: the latter is OpenAI's sampling seed on the chat route, and one name for two knobs on sibling endpoints is a trap.

Responses

200 How this request would route. A 200 with `would_dispatch: false` is the normal way to report "this would be refused" -- the preview succeeded, the request it describes would not.
requested stringrequired
strategy string

direct, fallback for a models[] chain, or latest for a ~alias.

would_dispatch booleanrequired

Whether a real request with this body would reach a provider at all.

limit_warning string | null

Set when the key is at or past a SOFT ceiling: the request would be served, with this sentence in its x-autonomous-relay-limit-warning header.

refusal object | null

Why not, in the order the pipeline refuses: a budget binds before a filter runs, and both before routing is consulted. Null when would_dispatch is true.

ordering enum

Whether cascade is the order or one draw from it.

§8.9.1's default ranking is a weighted random shuffle, deliberately: a deterministic cheapest-first router starves every provider but one. So sampled means the real request draws again and may order the platform attempts differently — reproduce this draw with the same routing_seed. deterministic means the order is fixed (an explicit sort, a :floor/:nitro variant, or too few platform attempts to shuffle) and a real request will match it.

One of: deterministic, sampled
routing_seed string

The seed this preview actually used, echoed so an unseeded call is still reproducible.

winner RoutePreviewCandidate | null
cascade RoutePreviewCandidate[]required

The endpoints a real request would try, in order, after the sticky hoist and cut to the same fallback depth.

beyond_fallback_depth integer

Endpoints that survived every filter but sit past the fallback depth: eligible, and still unreachable.

endpoints object
refused object[]

One entry per excluded endpoint, carrying the FIRST constraint that removed it. unsatisfied_filters says which constraints emptied the set; this says which endpoints each one cost you.

unsatisfied_filters string[]

The same strings §7.7's 404 would carry.

unresolved_models string[]

Slugs in the chain that named no model we have — a typo, or a model since withdrawn.

guardrails object
content_filter object | null

Null when no messages were supplied. labels name the detectors that matched; the matched text never travels.

sticky object
400 `model is required`.
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Run an evaluation

POST /v1/evals

A dataset, a task and up to 8 models (at most 400 cells), producing a scored comparison with per-cell cost and latency (§17.6). Every cell is a real POST /v1/chat/completions on your credentials -- the same rate limit, budgets, guardrails, routing and billing as if you had sent it, and a request row of its own -- so an evaluation is the same account, keys and routing you would run in production. A cell your rate limit refuses waits for the window and is tried again.

The answer streams as NDJSON: one {"type": "cell", ...} line per cell as it completes (four at a time), then {"type": "summary", ...} per model. Nothing is stored: the dataset arrives with the request, the outputs go back in the stream, and there is no run to fetch later.

Scorers: none, exact and contains (against each item's expected; case and spacing ignored unless case_sensitive), and json (the answer parses as JSON, code fences allowed). There is no regex scorer: a caller's pattern run over a model's answer can stall a backtracking engine, so pattern scoring waits for a linear-time one.

Authentication

Inference key (ar-v1-…) · Dashboard session

Request body

models string[]required
dataset object[]required
task object
scorer object

Responses

200 NDJSON: cell lines, then the summary.
400 A malformed body, named; more than 400 cells; an unknown scorer; or a scorer that needs `expected` where an item has none.
401 No key; an invalid, disabled or expired key; or an invalid session.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Catalog

Models, their endpoints and providers. Public.

List models

GET /v1/models

Public. Rate limited per client address: 120 a minute, 20,000 a day.

Authentication

None: this endpoint is public.

Parameters

limit integer · query

Page size.

offset integer · query

Rows to skip.

q string · query

A case-insensitive substring of the name or id.

model_authors string · query

Comma-separated authors, e.g. anthropic,openai.

context integer · query

Minimum context length, in tokens.

output_modalities string · query

Comma-separated; the model produces ANY of them: text, image, audio, video, embedding, rerank. Other values are ignored.

input_modalities string · query

Comma-separated; the model accepts ALL of them: text, image, file, audio, video. Other values are ignored.

max_prompt_price number · query

The most the cheapest endpoint may charge for input, in USD per million tokens. total_count counts after it.

free enum · query

true: only models whose every price is zero (the catalogue's is_free).

sort enum · query

Anything else sorts by id.

Responses

200 A page of models.
id stringrequired

author/slug.

canonical_slug string
name stringrequired
created integer | null

Unix seconds.

description string | null
context_length integer | null
architecture object | null

Input and output modalities.

supported_parameters string[] | null
default_parameters object | null
resale_permitted enum
One of: yes, no, via_reseller, unknown
byok_only boolean
is_free boolean
deprecation_date string | null
pricing object

The cheapest endpoint's price. Present on the list only.

429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get a model

GET /v1/models/{author}/{slug}

Public and rate limited, as the list is. pricing is absent here; the endpoints route has every endpoint's prices.

Authentication

None: this endpoint is public.

Parameters

author string · pathrequired

e.g. anthropic.

slug string · pathrequired

e.g. claude-sonnet-5.

Responses

200 OK.
id stringrequired

author/slug.

canonical_slug string
name stringrequired
created integer | null

Unix seconds.

description string | null
context_length integer | null
architecture object | null

Input and output modalities.

supported_parameters string[] | null
default_parameters object | null
resale_permitted enum
One of: yes, no, via_reseller, unknown
byok_only boolean
is_free boolean
deprecation_date string | null
pricing object

The cheapest endpoint's price. Present on the list only.

404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List a model's endpoints

GET /v1/models/{author}/{slug}/endpoints

Public and rate limited. Every provider serving the model, with its price, measured uptime and latency, and tool-call reliability.

Authentication

None: this endpoint is public.

Parameters

author string · pathrequired

e.g. anthropic.

slug string · pathrequired

e.g. claude-sonnet-5.

Responses

200 OK.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

A model's availability per provider over the last week

GET /v1/models/{author}/{slug}/availability

Public and rate limited. For each provider that served the model in the last 7 UTC days, one figure per day: the share of dispatches it answered without failing on its side (a 5xx, unreachable, a broken stream). A caller's own errors -- a bad request, their key's rate limit -- do not count against it. A day with fewer than min_dispatches dispatches, or from fewer than min_organizations organisations, is null: not measured.

Authentication

None: this endpoint is public.

Parameters

author string · pathrequired

e.g. anthropic.

slug string · pathrequired

e.g. claude-sonnet-5.

Responses

200
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

How a model's input and output prices have changed, per provider

GET /v1/models/{author}/{slug}/price-history

Public and rate limited. One series per provider and SKU (prompt, completion), oldest first: each point is a USD-per-token price and the time it took effect (from) and was replaced (to, null for the current price). Prices are effective-dated, so every change since the catalogue began recording them is here; an unchanged price written again is one point.

Authentication

None: this endpoint is public.

Parameters

author string · pathrequired

e.g. anthropic.

slug string · pathrequired

e.g. claude-sonnet-5.

Responses

200
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Everything the home page shows

GET /v1/home

Public and rate limited, cached 30 seconds. The first 60 models, the providers, the purchase fee schedule, and the endpoints of the first six models -- each exactly as its own operation returns it. A part that could not be read is null; an endpoints read that failed is left out.

Authentication

None: this endpoint is public.

Responses

200 OK.
models object | nullrequired
providers Provider[] | nullrequired
fees object | nullrequired
endpoints object[]required
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Whether free models work without a provider key here

GET /v1/free-tier

Public and rate limited. Whether this deployment serves the catalogue's free models without a provider key of your own, on its own key: available, the daily request limit per organisation, and the models it applies to. available is false, and models empty, when it does not -- then every model, free ones included, runs on your own provider key.

Authentication

None: this endpoint is public.

Responses

200 OK.
available boolean
daily_limit integer
models string[]
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Models added and prices changed in the catalogue lately

GET /v1/catalog/changes

Public and rate limited. Ready models added in the last days days (default 30, at most 90), newest first, and every input (prompt) or output (completion) per-token price that changed on an endpoint in that window -- rises and drops -- with the price before and after, in USD per token. A change is the importer replacing an endpoint's price: the same definition the price_drop alert uses.

Authentication

None: this endpoint is public.

Parameters

days integer · query

Responses

200
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Ask to be emailed when a model's price drops

POST /v1/models/{author}/{slug}/price-alerts

Public; no account. Double opt-in: the address is sent one confirmation email, and nothing else until its link is clicked; an unconfirmed request is deleted after 7 days. Once confirmed, an email is sent when the model's input or output price drops on any provider -- once per drop, several models' drops in one email -- each with a one-click unsubscribe link. The answer is the same 202 whether or not an email was sent (an address already subscribed, over its limit of 20 models or of requests per day), so it says nothing about any address. Rate limited per client address and per email address.

Authentication

None: this endpoint is public.

Parameters

author string · pathrequired

e.g. anthropic.

slug string · pathrequired

e.g. claude-sonnet-5.

Request body

email stringrequired

The address to email.

lang enum

The language of the emails. Default en.

One of: en, fr

Responses

202 Check your inbox.
status enumrequired
One of: check_inbox
message stringrequired
400 `email` is not an email address.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
503 Email is not configured on this deployment (`service_unavailable`).

Whether price-drop emails can be requested here

GET /v1/price-alerts/availability

Public. available is false on a deployment with no email configured, where subscribing answers 503.

Authentication

None: this endpoint is public.

Responses

200 OK.
available booleanrequired
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Confirm a price-drop email subscription

GET /v1/price-alerts/confirm

The link in the confirmation email. Valid for 7 days, and idempotent while valid. Redirects to the model's page with price_alert=confirmed, or to /models?price_alert=invalid for an unknown or expired link.

Authentication

The token in the link we sent (query parameter token)

Parameters

token string · queryrequired

From the confirmation email.

Responses

303 To the dashboard, with the outcome.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Unsubscribe from price-drop emails (link)

GET /v1/price-alerts/unsubscribe

The link in every price-alert email; no sign-in. Deletes the subscription -- or, for an email's "stop all" link, every subscription of the address -- and the address once it has none. Redirects to the model's page (or /models) with price_alert=unsubscribed, or price_alert=invalid for a link already used or unknown. Never refused by region: it only deletes.

Authentication

The token in the link we sent (query parameter token)

Parameters

token string · queryrequired

From the email.

Responses

303 To the dashboard, with the outcome.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Unsubscribe from price-drop emails (one-click, RFC 8058)

POST /v1/price-alerts/unsubscribe

What a mail client POSTs to the List-Unsubscribe URL, with the body List-Unsubscribe=One-Click (form-encoded; ignored). The same deletion as the link.

Authentication

The token in the link we sent (query parameter token)

Parameters

token string · queryrequired

From the email's List-Unsubscribe header.

Responses

200 OK.
unsubscribed booleanrequired
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List providers

GET /v1/providers

Public and rate limited.

Authentication

None: this endpoint is public.

Responses

200 OK.
slug stringrequired
name stringrequired
hq_country string | null
datacenters string[] | null
privacy_policy_url string | null
tos_url string | null
status_page_url string | null
models_served integer

In GET /v1/providers only: how many ready models the provider has an active endpoint for -- the models GET /v1/providers/{slug} lists. Zero for a provider in the directory that serves nothing yet.

429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Model rankings by tokens processed

GET /v1/rankings/models

Public and rate limited. Tokens processed per model -- prompt, completion, reasoning and cache, by the model that served them -- over day (since 00:00 UTC), week (7 days), month (30 days), or trending (the largest increase, this week against the week before). A measure of use, not of quality. Aggregated only, and a model is listed only once at least min_organizations (3) distinct organisations used it in the period; in trending, last week's figure is null when last week was below that threshold too. Recomputed at most every ten minutes.

Authentication

None: this endpoint is public.

Parameters

range enum · query

day, week (default), month or trending.

Responses

200 The ranking.
rank integer
model string
name string | null
author string | null
tokens integer
share number
available boolean

Whether the model is still in the catalogue. A retired model keeps its rank, since usage is history, but has no page to link to.

prior_tokens integer | null

Trending only.

change_tokens integer | null

Trending only.

400 An unknown `range`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

The apps directory: apps by tokens processed

GET /v1/rankings/apps

Public and rate limited. Apps are what requests name in HTTP-Referer; each is listed by its domain only (paths merged, www. dropped) -- never the X-Title or categories a caller sends, which anyone can set. A domain is listed once it has at least min_requests (100) completed requests in the period; apps whose first request sent X-App-Visibility: hidden never are, nor are local or private hosts. week (7 days, the default) or month (30). Recomputed at most every ten minutes.

Authentication

None: this endpoint is public.

Parameters

range enum · query

week (default) or month.

Responses

200 The directory.
rank integer
domain string
tokens integer
top_models string[]
retired_models string[]
400 An unknown `range`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get a provider and the models it serves

GET /v1/providers/{slug}

Public and rate limited. The directory row, and every ready model the provider has an active endpoint for, with the endpoint's prompt and completion price, context, quantization, region and published uptime -- the same figures as the model's own endpoints list.

Authentication

None: this endpoint is public.

Parameters

slug string · pathrequired

e.g. anthropic.

Responses

200 OK.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Keys

API keys.

Inspect the calling key

GET /v1/key

For an inference key: its limit, what remains of it, and what it has spent plus what its in-flight requests have reserved.

Authentication

Inference key (ar-v1-…)

Responses

200 OK.
label string | null
limit number | null

The key's spend limit in USD, or null for none.

limit_remaining number | null

Ceiling minus usage, measured exactly as the spend gate measures it: within the reset window, including spend on your own provider credential only when include_byok_in_limit is set, plus the worst-case reservation of every request on this key still in flight -- released or replaced by the actual amount at settlement, so this can go down. Not a billing total -- see /v1/credits for money.

usage number

Spend counted against this key ceiling, measured exactly as the spend gate measures it: within the reset window, including spend on your own provider credential only when include_byok_in_limit is set, plus the worst-case reservation of every request on this key still in flight -- released or replaced by the actual amount at settlement, so this can go down. Not a billing total -- see /v1/credits for money.

limit_reset string | null
limit_mode enum

hard (the default) refuses a request once the key is at or past its ceiling, with 403 budget_exceeded. soft serves it and adds an x-autonomous-relay-limit-warning header carrying the same sentence.

One of: hard, soft
include_byok_in_limit boolean

Count spend made on your own provider credential toward this ceiling, at IMPUTED LIST PRICE -- what the call would have cost on platform capacity, not the platform fee charged. Defaults to true: every model is BYOK-only, so a ceiling that skipped this spend would count nothing and could never bind. Set false to count only platform spend.

rpm_limit integer | null

The key's own requests per minute, counted in calendar minutes on top of the account quota; null for none. Inference keys only.

tpm_limit integer | null

The key's own tokens (prompt + completion) per minute; null for none. Requests are refused once the minute's count reaches it, and a request's tokens are counted when it finishes, so requests in flight can overshoot it. Inference keys only.

is_free_tier boolean
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List keys

GET /v1/keys

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

include_disabled boolean · query

Include revoked keys.

Responses

200 OK.
hash string

Identifies the key in DELETE /v1/keys/{hash}. Not the key itself.

prefix string
name string | null
label string | null
class enum
One of: inference, management
disabled boolean
limit number | null

USD.

limit_nanodollars string | null

The same limit, raw.

limit_reset string | null
limit_mode enum

hard (the default) refuses a request once the key is at or past its ceiling, with 403 budget_exceeded. soft serves it and adds an x-autonomous-relay-limit-warning header carrying the same sentence.

One of: hard, soft
include_byok_in_limit boolean
rpm_limit integer | null

The key's own requests per minute, counted in calendar minutes on top of the account quota; null for none. Inference keys only.

tpm_limit integer | null

The key's own tokens (prompt + completion) per minute; null for none. Requests are refused once the minute's count reaches it, and a request's tokens are counted when it finishes, so requests in flight can overshoot it. Inference keys only.

cache_enabled boolean

Whether this key's non-streaming chat completions are answered from the response cache. Off by default.

cache_ttl_seconds integer

How long an answer stays cached. Kept while the cache is off.

workspace_id string | null

The workspace (client) the key belongs to.

usage number | null

USD counted against the ceiling in its current window, measured exactly as the spend gate measures it (see KeyInfo.usage). Null for a key with no ceiling, or a revoked one.

limit_remaining number | null

Ceiling minus usage. Negative when a soft ceiling has been passed. Null when there is no ceiling.

limit_resets_at string | null

When the current window ends. Null for a lifetime ceiling or none.

created_at string
updated_at string
expires_at string | null
external_user string | null
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Create a key

POST /v1/keys

The plaintext key is in this response and never again: only a salted hash is stored.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

name string
limit number

A spend limit in USD. Omit for none.

limit_reset string | null

The window the ceiling covers, in UTC calendar terms. Null means lifetime: the ceiling never resets.

One of: daily, weekly, monthly, lifetime, null
limit_mode enum

hard (the default) refuses a request once the key is at or past its ceiling, with 403 budget_exceeded. soft serves it and adds an x-autonomous-relay-limit-warning header carrying the same sentence.

One of: hard, soft
Default: "hard"
include_byok_in_limit boolean

Count spend on your own provider credential toward the ceiling, at list price. On by default: every model is BYOK-only, so without it the ceiling would count nothing.

Default: true
workspace_id string

Put the key in this workspace (client). Omit for the caller's own workspace. Another organisation's id is a 404.

class enum
One of: inference, management
Default: "inference"
expires_at string

When the key stops authenticating (401 key_expired). With a time zone, in the future, within ten years. Omit for a key that does not expire.

rpm_limit integer | null

The key's own requests per minute, counted in calendar minutes on top of the account quota; null for none. Inference keys only.

tpm_limit integer | null

The key's own tokens (prompt + completion) per minute; null for none. Requests are refused once the minute's count reaches it, and a request's tokens are counted when it finishes, so requests in flight can overshoot it. Inference keys only.

cache_enabled boolean

Answer this key's repeated non-streaming chat completions from the response cache, at no cost. Inference keys only. An identical request gets the same answer, whatever its temperature.

Default: false
cache_ttl_seconds integer

How long an answer stays cached.

Default: 3600

Responses

201 Created.
key stringrequired

The key. Store it now.

hash stringrequired
name string | null
class enumrequired
One of: inference, management
expires_at string | null
rpm_limit integer | null

The key's own requests per minute, counted in calendar minutes on top of the account quota; null for none. Inference keys only.

tpm_limit integer | null

The key's own tokens (prompt + completion) per minute; null for none. Requests are refused once the minute's count reaches it, and a request's tokens are counted when it finishes, so requests in flight can overshoot it. Inference keys only.

cache_enabled boolean
cache_ttl_seconds integer
400 A field out of range or of the wrong type, named in the message.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Change a key's spend limit, rates or expiry, or pause and resume it

PATCH /v1/keys/{hash}

Changes the ceiling, its window, its mode, whether BYOK spend counts, the key's requests and tokens per minute, the workspace, a pause or the expiry. Only these: a key's class, name and secret are fixed at creation. At least one field is required. The new ceiling applies at once on the instance serving this call and within the auth cache's 10-second TTL everywhere else.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

hash string · pathrequired

The key's hash, from the list.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

limit number | null

The new ceiling in USD; null removes it.

limit_reset string | null

The window the ceiling covers, in UTC calendar terms. Null means lifetime: the ceiling never resets.

One of: daily, weekly, monthly, lifetime, null
limit_mode enum

hard (the default) refuses a request once the key is at or past its ceiling, with 403 budget_exceeded. soft serves it and adds an x-autonomous-relay-limit-warning header carrying the same sentence.

One of: hard, soft
include_byok_in_limit boolean
workspace_id string

Move the key to this workspace of the same organisation.

paused boolean

false resumes a key paused by a runaway flag or by hand; true pauses it by hand. A paused key answers 403 key_paused.

expires_at string | null

Set or move the expiry (with a time zone, in the future, within ten years); null removes it. Refused on the calling key and on a dashboard sign-in key, which would lock their holder out.

rpm_limit integer | null

The key's own requests per minute, counted in calendar minutes on top of the account quota; null for none. Inference keys only.

tpm_limit integer | null

The key's own tokens (prompt + completion) per minute; null for none. Requests are refused once the minute's count reaches it, and a request's tokens are counted when it finishes, so requests in flight can overshoot it. Inference keys only.

cache_enabled boolean

Turn the response cache on or off for this key. Inference keys only. Turning it off stops lookups at once; entries already stored expire on their TTL, or go now with DELETE /v1/cache.

cache_ttl_seconds integer

How long answers stored from now on stay cached.

Responses

200 The key's ceiling as it now stands.
hash stringrequired
name string | null
class enum
One of: inference, management
limit number | null

USD.

limit_reset string | null
limit_mode enum
One of: hard, soft
include_byok_in_limit boolean
paused boolean
paused_reason enum
One of: runaway, manual, null
expires_at string | null
rpm_limit integer | null

The key's own requests per minute, counted in calendar minutes on top of the account quota; null for none. Inference keys only.

tpm_limit integer | null

The key's own tokens (prompt + completion) per minute; null for none. Requests are refused once the minute's count reaches it, and a request's tokens are counted when it finishes, so requests in flight can overshoot it. Inference keys only.

cache_enabled boolean
cache_ttl_seconds integer
400 A field out of range or of the wrong type, named in the message.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Revoke a key

DELETE /v1/keys/{hash}

Disables the key and ends every dashboard session it created.

Propagation is bounded at 10 seconds (§17.4: "publish that TTL"). The instance serving this call stops accepting the key immediately; any other instance stops within the auth cache's TTL. That cache serves no stale window — past ten seconds the entry is refused and the caller waits on the database — so ten seconds is the real bound, not the point at which a refresh merely begins.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

hash string · pathrequired

The key's hash, from the list.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
deleted anyrequired
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Describe the response cache

GET /v1/cache

The organisation's live response-cache entries and their size. The cache is turned on per inference key (cache_enabled); available is false while the deployment does not run it, in which case nothing is cached whatever a key says.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
available booleanrequired
entries integerrequired
bytes integerrequired

The size of the stored answers, before encryption.

401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Purge the response cache

DELETE /v1/cache

Deletes every cached answer of the organisation at once; the next request of each kind is a miss. Owner and Admin only (org:write).

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
deleted integerrequired

Entries deleted.

401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Usage

What was requested, and what it cost.

List recent requests

GET /v1/activity

Newest first, keyset-paginated. Cost is summed per row, over the window.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

limit integer · query

Page size.

days integer · query

The window, ending now.

cursor string · query

The next_cursor of the previous page. Opaque; do not parse it.

model string · query

The model that served.

provider string · query

A provider slug.

workspace string · query

One client: a workspace id. A malformed id is a 400.

agent string · query

One agent: the X-Autonomous-Relay-Agent value.

tag string · query

Requests a classifier tagged with this.

status string · query

The request status, e.g. complete.

byok boolean · query

BYOK requests only, or none.

app string · query

An app_id.

Responses

200 A page of requests.
id string
created_at string
requested_model string
served_model string | null

Null until routing picks one; differs from requested_model when a fallback fired.

provider_slug string | null
status string
finish_reason string | null

Normalized to five values. native_finish_reason carries the provider's own.

One of: stop, length, tool_calls, content_filter, error, null
native_finish_reason string | null
is_byok boolean
is_cache_hit boolean
cancelled boolean
streamed boolean
quantization string | null
ttft_ms integer | null
generation_ms integer | null
routing_overhead_ms integer | null

Total time minus upstream provider time -- the latency this gateway added (§18). Null when nothing was dispatched, and on rows recorded before it was measured.

error_code string | null

The provider's own code where a provider refused, or a router.* reason code where this gateway is what failed -- a credential it could not open, a request it could not express. A stream that broke after it had committed is recorded on the upstream attempt, not here.

error_message string | null

The failure in English, for a developer. Where a provider refused, this names the provider, its HTTP status and its error code -- never the provider's own text, which can quote the prompt and is returned only in the response to the request itself. Where this gateway failed, its own sentence.

total_cost number

US dollars.

app_id string | null
app_title string | null
http_referer string | null
workspace_id string | null

The client (workspace) of the key that made the request.

agent string | null

The X-Autonomous-Relay-Agent the request named, if any.

tags string[]
400 `cursor is malformed`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Export a month of requests as CSV

GET /v1/activity/export.csv

One row per request in a UTC calendar month, for the account or one client (workspace). Columns: created_at, request_id, client, key, requested_model, served_model, provider, status, error_code, byok, prompt_tokens, completion_tokens, billed_usd (what this account was charged), list_price_usd (the tokens at catalogue list price: under BYOK, the number a client is re-billed on and the one budgets measure) and agent. Text cells that a spreadsheet would run as a formula are prefixed with a single quote. Capped at 200,000 rows, after which a # truncated line ends the file. At most 6 exports a minute per key.

Needs the Pro ($29/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

month string · query

YYYY-MM, UTC. Default: the current month.

workspace string · query

One client: a workspace id. Another organisation's is a 404.

Responses

200 The CSV, as an attachment.
400 `month must be YYYY-MM` or `workspace must be a workspace id`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 `subscription_required`: the organisation's plan does not include this. The message names the plan it needs; what was set up keeps working.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 More than 6 exports a minute.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Query usage analytics

POST /v1/analytics/query

Aggregates over your requests and what they cost -- the rows Activity and Logs read, so the numbers agree. Name at least one metric, up to two dimensions to group by, up to twenty filters on dimensions, a time range (default the last 7 days, at most 92) and a granularity for a series. group_limit keeps the top groups by the first metric over the whole range, so a series has the same groups in every bucket. GET /v1/analytics/meta lists every metric and dimension. A seat limited to some workspaces queries those alone.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

metrics string[]required

e.g. requests, spend_usd, total_tokens, error_rate, avg_ttft_ms.

dimensions string[]

e.g. model, provider, api_key, workspace, agent.

filters object[]
time_range object
granularity enum
One of: none, hour, day, week, month
Default: "none"
limit integer
Default: 1000
group_limit integer
order_by object

Responses

200 The aggregates.
400 An unknown metric or dimension, too many of anything, a malformed filter or time range, or an order_by field that was not requested.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List analytics metrics and dimensions

GET /v1/analytics/meta

Every metric, dimension, filter operator and granularity POST /v1/analytics/query accepts, and its limits.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 What can be queried.
metrics object[]
dimensions object[]
operators string[]
granularities string[]
limits object
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List analytics metrics and dimensions (alias)

GET /v1/analytics/metrics

The same as GET /v1/analytics/meta, under the name the design requirements use.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 What can be queried.
metrics object[]
dimensions object[]
operators string[]
granularities string[]
limits object
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Scrape usage as Prometheus metrics

GET /v1/metrics

The caller's organisation in the Prometheus text exposition format (version 0.0.4), for a Prometheus scrape_config with the management key as bearer_token. Counters count finished requests since the start of the current UTC month and reset to zero at each month boundary, which rate() and increase() treat as an ordinary counter reset; autonomousrelay_counters_since_timestamp_seconds says when. Families: autonomousrelay_requests_total{model,provider,status}, autonomousrelay_tokens_total{model,provider,type} (type is prompt or completion), autonomousrelay_spend_usd_total{model,provider} (USD billed), the autonomousrelay_request_duration_seconds{model,provider} histogram, and the autonomousrelay_active_keys{class} gauge. The 25 models with the most requests keep their own model label; the rest are summed as other. A seat limited to some workspaces sees those alone. Computed at most every 15 seconds per organisation; a faster scrape reads the same numbers.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 The metrics.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get activity totals

GET /v1/activity/summary

The KPI cards (§14.9), with the prior period of equal length beside them and a daily series for the sparklines. Both periods are read in one pass, so they cannot disagree about where "now" was.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

days integer · query

The window, ending now. The prior period is the same length again.

workspace string · query

One client: a workspace id. A malformed id is a 400.

Responses

200 Totals for the window.
requests integer
errors integer

Requests that ended with an error code. Counted zero on deployments older than the reason codes, where no code was recorded.

byok_requests integer
spend number

US dollars.

tokens integer

Prompt plus completion. cache_read and reasoning are subsets of those and are not added again.

prompt_tokens integer
cached_tokens integer

Prompt tokens served from the provider's cache -- a subset of prompt_tokens.

cache_hit_rate number | null

cached_tokens / prompt_tokens. Null when there were no prompt tokens, which is not the same as a rate of zero.

blended_per_million number | null

USD per million tokens, blended across every model and provider in the window. Null when there were no tokens.

401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get one request

GET /v1/activity/{id}

Everything recorded about one request (§14.10): the list row, plus the key that made it, its session and run, the usage lines it was billed on, and every dispatch to a provider in order. Metadata only -- stored content is GET /v1/generation/{id}/content. Another organisation's id is a 404.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The request id (gen-…).

Responses

200 The request.
400 `id must be a request id`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List upstream provider requests

GET /v1/activity/upstream

One row per dispatch to a provider, newest first and keyset-paginated. A request that failed over twice appears three times -- filter by request to see a single generation's attempts in order.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

limit integer · query

Page size.

days integer · query

The window, ending now.

cursor string · query

The next_cursor of the previous page. Opaque; do not parse it.

workspace string · query

One client: a workspace id. The dispatches made for its requests. A malformed id is a 400.

request string · query

A generation id: every dispatch made for it.

provider string · query

A provider slug.

status enum · query

Whether the dispatch committed.

Responses

200 A page of dispatches.
id string

Opaque; the row's identity, not the request's.

request_id string

The generation this dispatch was made for. Several rows share it when a fallback fired.

created_at string
attempt integer

1-indexed position in the cascade.

model_id string
provider_slug string
endpoint_id string
is_byok boolean
status enum

Whether the dispatch committed. A stream that shipped bytes and then failed is ok with an error_message.

One of: ok, error
http_status integer | null

The upstream status. Null when the dispatch never reached the provider.

error_code string | null

The provider's own code where the provider answered, or a router.* reason code where this gateway is what failed -- a credential it could not open, a request it could not express, a stream that broke after it had committed.

error_message string | null

As on the request: the provider, status and code where the provider refused; never its own text.

duration_ms integer | null

Time inside the provider call. Null when nothing was dispatched.

ttft_ms integer | null
400 `cursor is malformed` or `workspace must be a workspace id`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List conversations

GET /v1/activity/sessions

Generations grouped by session_id, most recently active first. Only requests that carried one are included.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

limit integer · query

Page size.

days integer · query

The window, ending now.

cursor string · query

The next_cursor of the previous page. Opaque; do not parse it.

workspace string · query

One client: a workspace id. A malformed id is a 400.

Responses

200 A page of conversations.
session_id string
generations integer

Requests in this conversation, within the window.

errors integer
started_at string
last_seen_at string
spend number

US dollars.

tokens integer
models string[]

Every model the conversation was served by. More than one means it switched mid-session.

providers string[]
400 `cursor is malformed` or `workspace must be a workspace id`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get one request's record

GET /v1/generation

Which endpoint served it, at which price version, and what each SKU cost -- enough to recompute the charge.

Not the content. Prompts and completions are stored only when your organisation has enabled input/output logging (§13.1), which is off by default and never retroactive; when it is on, the content is read from GET /v1/generation/{id}/content, not from here.

Authentication

Inference key (ar-v1-…)

Parameters

id string · queryrequired

The id of a chat completion.

Responses

200 OK.
id string
model string | null

The model that served.

provider_name string | null
endpoint_id string | null
quantization string | null
total_cost number

US dollars.

finish_reason string | null

Normalized to five values. native_finish_reason carries the provider's own.

One of: stop, length, tool_calls, content_filter, error, null
native_finish_reason string | null
streamed boolean
cancelled boolean
is_byok boolean
latency integer | null

Time to first token, in milliseconds.

generation_time integer | null

Milliseconds.

price_version_ids integer[]
usage_lines object[]
app_id string | null
app_title string | null
http_referer string | null

Scheme and host only. The path, query string and fragment are never stored on a request, because that is where an app puts the user it is serving.

user_agent string | null
created_at string
400 `id is required`.
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get one generation's stored prompt and completion

GET /v1/generation/{id}/content

§13.1. Returns content only if your organisation had input/output logging enabled when the generation was made.

Stored encrypted (AES-256-GCM, data key wrapped by Cloud KMS) in a table separate from request metadata, under an AAD bound to your organisation — a credential ciphertext cannot be read here and content cannot be read from the credential path. Deleted automatically past your configured retention.

This is the only read path for stored content, and it is yours. §13.1 forbids using it for training or analytics, and there is no internal reader.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The generation id (gen-…).

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
request_id stringrequired
content objectrequired

The request messages, the completion, and any tool calls.

401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Read a generation's scores

GET /v1/generation/feedback

Every score your organisation's keys attached to the generation, oldest first; an empty list when there are none. Another organisation's id is a 404.

Authentication

Inference key (ar-v1-…)

Parameters

id string · queryrequired

The generation id (gen-…).

Responses

200 OK.
id stringrequired

The generation id (gen-…).

value numberrequired

From -1 to 1: thumbs down -1, thumbs up 1.

label string | nullrequired

A slug such as helpful, or null.

yours booleanrequired

Whether the calling key submitted this score. Which key did is never returned.

created_at stringrequired
updated_at stringrequired
400 `id is required` or `id must be a generation id`.
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Score a generation

POST /v1/generation/feedback

Attach a score to a request your organisation made, so you can see which models your users rate well. value is a number from -1 to 1 -- thumbs down -1, thumbs up 1, a finer rating mapped into the range. label is an optional slug (helpful, wrong_answer).

One score per generation per key: posting again from the same key replaces it. Any inference key of the organisation that made the request may score it, as any may read it with GET /v1/generation; another organisation's id is a 404.

No free text. A comment is refused with a 400: a comment is content an end user typed, and the gateway stores no content.

Authentication

Inference key (ar-v1-…)

Request body

id stringrequired

The generation id (gen-…).

value numberrequired
label string | null

Responses

200 The score as stored.
id stringrequired

The generation id (gen-…).

value numberrequired

From -1 to 1: thumbs down -1, thumbs up 1.

label string | nullrequired

A slug such as helpful, or null.

yours booleanrequired

Whether the calling key submitted this score. Which key did is never returned.

created_at stringrequired
updated_at stringrequired
400 A value outside -1..1, a label that is not a slug, a `comment`, or an unknown field.
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Feedback by model

GET /v1/feedback/summary

The average score and the number of scores, by the model that served the request, over requests made in the last days days (default 30, at most 90). Most-scored first, at most 100 models. A workspace-limited person sees their workspaces' requests only.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

days integer · query

The window, ending now, by when the request was made.

Responses

200 Scores by model.
model stringrequired
count integerrequired
average numberrequired
positive integerrequired

Scores above 0.

negative integerrequired

Scores below 0.

401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Credits

Balance and purchases.

Get the balance

GET /v1/credits

Held credits are reserved by requests in flight and are shown apart from the available balance.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
total_credits number

US dollars.

held_credits number

US dollars.

available_credits number

US dollars.

credit_line number

US dollars.

401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get the purchase fee schedule

GET /v1/credits/fees

Public. While nothing a credit can buy exists (BYOK-only), purchasable is false and the fee is reported as 0.

Authentication

None: this endpoint is public.

Responses

200 OK.
fee_bps integer

Basis points.

fee_min number

US dollars.

min_purchase number

US dollars.

configured boolean

A payment processor is configured.

purchasable boolean

A purchased balance could actually be spent.

429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List purchases

GET /v1/credits/history

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

limit integer · query

Page size.

cursor string · query

The next_cursor of the previous page. Opaque; do not parse it.

Responses

200 A page of purchases.
id string
created_at string
provider string
status string
gross number

US dollars.

fee number

US dollars.

net number

US dollars.

tax_jurisdiction string | null
has_document boolean
400 `cursor is malformed`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get a purchase's invoice or receipt

GET /v1/credits/purchases/{id}/invoice

Resolved from Stripe on demand; only the checkout session id is stored.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

A purchase id.

Responses

200 OK.
kind enum
One of: invoice, receipt
url string
pdf_url string | null
number string | null
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
409 The document is not ready yet; try again shortly. The body reports `code: 400`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 Stripe could not be reached.
503 Billing is not configured on this deployment (`service_unavailable`).

Start a credit purchase

POST /v1/credits/checkout

Returns a Stripe Checkout URL. Refused with 409 while credits cannot be spent (BYOK-only, zero fee) -- checked before the body is read.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

amount numberrequired

USD.

Responses

200 OK.
url string
id string
fee number

US dollars.

400 `amount must be at least $2`, or no greater than $50,000.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
409 Credits cannot be purchased while nothing can spend them. The body reports `code: 400`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
503 Billing is not configured (`service_unavailable`).

Receive a Stripe event

POST /v1/webhooks/stripe

For Stripe, not for you. Credits on checkout.session.completed and checkout.session.async_payment_succeeded only when payment_status is paid, once per session; logs async_payment_failed; acknowledges everything else with 200.

Authentication

Stripe signature

Responses

200 Accepted. Not wrapped in `data`.
received anyrequired
400 `Invalid signature`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
503 The webhook secret is not configured (`service_unavailable`).

Billing

The plans, per organisation, any number of members; inference is never marked up. A 14-day trial of Team, then Pro ($29/month): guardrails (create, assign), presets, classifiers, usage-alert channels (not the runaway setting, a safety control) and the CSV export; Team ($99): Pro plus invitations, workspace members, a second workspace, report branding and client reports; Business ($399): Team plus single sign-on and SCIM; Enterprise: an invoiced annual contract. Without a plan the core keeps working -- inference on your own provider keys, API keys, activity, logs, members, the first workspace. Creating or changing a feature the plan does not include answers 402 `subscription_required`, naming the plan it needs. What was set up keeps running, stays readable and can be deleted.

The organisation's plan

GET /v1/billing/plan

trial (Team's features) for 14 days from creation; then the paid plan while its subscription is active, trialing or past_due (Stripe retrying a renewal): pro ($29/month: guardrails, usage alerts, exports, presets, classifiers), team ($99: Pro plus members, more workspaces and client reports), business ($399: Team plus SSO and SCIM), or enterprise (an invoiced annual contract, set up per customer in Stripe with tier: enterprise in the subscription metadata); limited otherwise. Each plan includes the ones before it. A feature the plan does not include answers 402 subscription_required when created or changed, naming the plan it needs; what was set up keeps working. Readable by every role.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
state enum
One of: trial, pro, team, business, enterprise, limited
trial_ends_at string
trial_days_left integer
subscription object | null

{ status, current_period_end } once the organisation has subscribed; Stripe's own status word.

price object

This plan's monthly price; Team's while the organisation has no self-serve plan.

plans object[]

Every plan the organisation can subscribe to, cheapest first.

configured boolean

False when STRIPE_SECRET_KEY is unset: subscribing answers 503.

401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Subscribe to a plan

POST /v1/billing/subscribe

Returns a Stripe Checkout URL for the plan named in plan -- pro ($29), team ($99, the default) or business ($399) a month, per organisation, card only. The plan changes when Stripe's webhook confirms the payment, not when this URL is created. Owner, Admin or Finance.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

plan enum

Defaults to team.

One of: pro, team, business

Responses

200 OK.
url string
400 `plan` is not one of `pro`, `team`, `business`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
409 Already on a paid plan: change it with POST /v1/billing/change-plan (Enterprise: by contract).
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 Stripe could not start the checkout; nothing was charged.
503 Billing is not configured (`service_unavailable`).

Change plan

POST /v1/billing/change-plan

Moves a live Pro, Team or Business subscription to another of the three. The new price is prorated on the next invoice. Answers 202: the plan changes when Stripe's customer.subscription.updated arrives, usually within seconds. Enterprise is set by contract and is not changed here. Owner, Admin or Finance.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

plan enumrequired
One of: pro, team, business

Responses

202 OK.
plan enum
One of: pro, team, business
status enum
One of: pending
note string
400 `plan` is not one of `pro`, `team`, `business`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
409 No paid plan to change yet, already on that plan, or on Enterprise.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 Stripe could not change the plan; nothing was changed.
503 Billing is not configured (`service_unavailable`).

Open the billing portal

POST /v1/billing/portal

Returns a Stripe billing portal URL: the card, invoices, and cancelling. Owner, Admin or Finance.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
url string
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
409 The organisation has never subscribed.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 Stripe could not open the portal.
503 Billing is not configured (`service_unavailable`).

BYOK

Your provider credentials. Required before anything routes.

List provider credentials

GET /v1/byok

Keys are never returned, by this or any route -- only key_hint.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
id string
provider_slug string
name string | null
key_hint string

The last characters of the key. The key itself is never returned.

tier enum
One of: prioritized, fallback
priority integer
fallback_policy enum
One of: platform, none_for_models, none_for_provider
model_filter object
allowed_models string[] | null

Model ids or * patterns, ANDed with model_filter.

allowed_api_key_hashes string[] | null

Only these API keys (their hash, as GET /v1/keys lists it) may use the credential.

allowed_user_ids string[] | null

Only keys these members are behind -- created, or signed in with -- may use the credential. A key no person is behind is outside the list.

disabled boolean
created_at string
last_used_at string | null
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Add a provider credential

POST /v1/byok

Encrypted with a per-credential key before it is stored. Nothing routes until you have one for the provider that serves your model.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

provider stringrequired

A provider slug with an adapter, e.g. anthropic.

api_key stringrequired
name string
tier enum
One of: prioritized, fallback
priority integer
fallback_policy enum
One of: platform, none_for_models, none_for_provider
model_filter object
allowed_models string[] | null

Model ids or * patterns, ANDed with model_filter.

allowed_api_key_hashes string[] | null

Only these API keys (their hash, as GET /v1/keys lists it) may use the credential.

allowed_user_ids string[] | null

Only keys these members are behind -- created, or signed in with -- may use the credential. A key no person is behind is outside the list.

Responses

201 Created.
id string
provider_slug string
name string | null
key_hint string

The last characters of the key. The key itself is never returned.

tier enum
One of: prioritized, fallback
priority integer
fallback_policy enum
One of: platform, none_for_models, none_for_provider
model_filter object
allowed_models string[] | null

Model ids or * patterns, ANDed with model_filter.

allowed_api_key_hashes string[] | null

Only these API keys (their hash, as GET /v1/keys lists it) may use the credential.

allowed_user_ids string[] | null

Only keys these members are behind -- created, or signed in with -- may use the credential. A key no person is behind is outside the list.

disabled boolean
created_at string
last_used_at string | null
400 `provider is required`, `api_key is required`, no adapter exists for the provider, or a scope field is malformed or names a key or person outside the organisation.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
503 BYOK key management is not configured on this deployment (`service_unavailable`).

Enable, disable or scope a provider credential

PATCH /v1/byok/{id}

disabled and the three scope fields can change; a field left out is unchanged, null makes it unrestricted. To change the key, add a new credential and delete this one.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

A credential id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

disabled boolean
allowed_models string[] | null

Model ids or * patterns, ANDed with model_filter.

allowed_api_key_hashes string[] | null

Only these API keys (their hash, as GET /v1/keys lists it) may use the credential.

allowed_user_ids string[] | null

Only keys these members are behind -- created, or signed in with -- may use the credential. A key no person is behind is outside the list.

Responses

200 OK.
id string
provider_slug string
name string | null
key_hint string

The last characters of the key. The key itself is never returned.

tier enum
One of: prioritized, fallback
priority integer
fallback_policy enum
One of: platform, none_for_models, none_for_provider
model_filter object
allowed_models string[] | null

Model ids or * patterns, ANDed with model_filter.

allowed_api_key_hashes string[] | null

Only these API keys (their hash, as GET /v1/keys lists it) may use the credential.

allowed_user_ids string[] | null

Only keys these members are behind -- created, or signed in with -- may use the credential. A key no person is behind is outside the list.

disabled boolean
created_at string
last_used_at string | null
400 Another field was patched, or a scope field is malformed or names a key or person outside the organisation.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Delete a provider credential

DELETE /v1/byok/{id}

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

A credential id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
deleted anyrequired
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Alerts

Usage alerts: where you are told a key is nearing its spend ceiling.

List usage-alert channels

GET /v1/alerts/channels

Where the organisation is told that a key crossed a share of its spend ceiling. email_available says whether this deployment can send email. A webhook's signing secret is never returned here: only once, when the channel is created.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 The channels.
id string
kind enum
One of: webhook, slack, email
target string
name string | null
thresholds integer[]
events enum[]

What the channel receives. usage (the default): key and agent limit crossings and runaway flags. price_drop: a model this organisation used in the last 30 days got cheaper (input or output tokens), announced once, within 15 minutes of the change.

disabled boolean
created_at string
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Add a usage-alert channel

POST /v1/alerts/channels

A key crossing any of thresholds (percent of its ceiling, default 50, 80 and 100), or an agent crossing them on one of its budgets (agent_limit.threshold), is announced once per threshold per ceiling window, on every channel that lists that threshold; a key that jumps several thresholds in one request is announced once, at the highest. A webhook target must be a public https URL on the default port; a slack target must be a https://hooks.slack.com/... incoming-webhook URL; email needs the deployment to be configured for it. Webhook deliveries are JSON, signed in x-autonomous-relay-signature: t=<unix>,v1=<hex>, where v1 is HMAC-SHA256 of <t>.<raw body> with the channel's signing_secret. At most 20 channels per organisation.

Needs the Pro ($29/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

kind enumrequired
One of: webhook, slack, email
target stringrequired

An https URL, or an email address for email.

name string
thresholds integer[]

Whole percentages from 1 to 100. Default [50, 80, 100].

events enum[]

What the channel receives. usage (the default): key and agent limit crossings and runaway flags. price_drop: a model this organisation used in the last 30 days got cheaper (input or output tokens), announced once, within 15 minutes of the change.

Responses

201 Created.
id string
kind enum
One of: webhook, slack, email
target string
name string | null
thresholds integer[]
events enum[]

What the channel receives. usage (the default): key and agent limit crossings and runaway flags. price_drop: a model this organisation used in the last 30 days got cheaper (input or output tokens), announced once, within 15 minutes of the change.

disabled boolean
created_at string
400 An unsafe or malformed target, a Slack target that is not hooks.slack.com, email not configured, bad thresholds, or the 20-channel cap.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 `subscription_required`: the organisation's plan does not include this. The message names the plan it needs; what was set up keeps working.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Change what a channel receives

PATCH /v1/alerts/channels/{id}

Only events: a new target is a new channel. Changing events keeps the channel and its webhook signing secret. A price_drop event is JSON {type, model, provider, sku, old_usd_per_million, new_usd_per_million, effective_at, at}, with x-autonomous-relay-event: price_drop, signed as every other delivery is.

Needs the Pro ($29/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The channel id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

events enum[]required

What the channel receives. usage (the default): key and agent limit crossings and runaway flags. price_drop: a model this organisation used in the last 30 days got cheaper (input or output tokens), announced once, within 15 minutes of the change.

Responses

200 OK.
id string
kind enum
One of: webhook, slack, email
target string
name string | null
thresholds integer[]
events enum[]

What the channel receives. usage (the default): key and agent limit crossings and runaway flags. price_drop: a model this organisation used in the last 30 days got cheaper (input or output tokens), announced once, within 15 minutes of the change.

disabled boolean
created_at string
400 `events must be a non-empty list of: usage, price_drop`, an unknown event, or a field other than `events`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 `subscription_required`: the organisation's plan does not include this. The message names the plan it needs; what was set up keeps working.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Remove a usage-alert channel

DELETE /v1/alerts/channels/{id}

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The channel id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
deleted anyrequired
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Send a test alert

POST /v1/alerts/channels/{id}/test

Delivers a test event now and reports the outcome, so a wrong URL or a receiver that rejects the payload shows at once. The attempt is recorded in the delivery log.

Needs the Pro ($29/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The channel id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
status enumrequired
One of: sent, failed, skipped
http_status integer | null

What the receiver answered, when it answered.

error string | null

Why it failed, in our words. The receiver's response body is never read.

401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 `subscription_required`: the organisation's plan does not include this. The message names the plan it needs; what was set up keeps working.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

What a runaway flag does

GET /v1/alerts/settings

alert: every flag is sent to the alert channels and nothing else happens (the default). pause: a flagged KEY is also paused -- it answers 403 key_paused until resumed with PATCH /v1/keys/{hash} {"paused": false}. An agent flag is never a pause: a tag is not a credential.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
runaway_action enumrequired
One of: alert, pause
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Choose what a runaway flag does

PUT /v1/alerts/settings

Requires config:write (Developer or above): this decides whether a heuristic may stop production traffic.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

runaway_action enumrequired
One of: alert, pause

Responses

200 OK.
runaway_action enumrequired
One of: alert, pause
400 A threshold out of range, named in the message.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Runaway-spend flags

GET /v1/alerts/runaways

A key or an agent (X-Autonomous-Relay-Agent) flagged because its last 15 minutes were far above its own previous 24 hours: spend at least 3x its usual 15 minutes and at least $5, or requests at least 5x usual and at least 300. Checked every five minutes; a subject is flagged at most once an hour, and each flag is sent to every alert channel as a spend.runaway event. With runaway_action = pause (see /v1/alerts/settings) a flagged key is also paused; action says which happened. The last 30 days, at most 100.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
id string
subject_kind enum
One of: key, agent
subject string

The agent tag, or the first 12 characters of the key hash.

key_name string | null
key_hash string | null

The full hash, for PATCH /v1/keys/{hash}; null for an agent.

key_paused boolean

Whether the key is paused right now (by this flag, a later one, or by hand).

action enum

What the sweep did with this flag.

One of: alert, paused
kind enum
One of: spend, rate
window_start string
window_end string
spend number

US dollars.

usual_spend number

US dollars.

requests integer
usual_requests number
created_at string
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Recent alert deliveries

GET /v1/alerts/deliveries

The last 50 delivery attempts, newest first, each with its outcome. Kept for 90 days.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
id string
channel_id string | null

Null once the channel has been removed.

channel_name string | null
channel_kind string | null
event enum
One of: key_limit.threshold, agent_limit.threshold, spend.runaway, run.paused, connection.skipped, price_drop, ledger.drift, test
threshold integer | null
status enum
One of: sent, failed, skipped
http_status integer | null
error string | null
key_name string | null
key_hash_prefix string | null
agent_tag string | null

For an agent-budget alert: the X-Autonomous-Relay-Agent value.

created_at string
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Files

Upload once, refer to by id. Off unless the deployment enables it, pending a privacy review.

List files

GET /v1/files

Unexpired files, newest first, as {"object": "list", "data": [...]}. Off unless the deployment sets FILES_ENABLED=true: uploaded files are customer content at rest, which waits on a privacy review; until then every files route is a 403 saying so.

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Responses

200 The files.
id string

file-…

object enum
One of: file
bytes integer
created_at integer
expires_at integer
filename string
purpose string
mime_type string

What the bytes are, whatever the name says.

status enum
One of: processed
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Upload a file

POST /v1/files

OpenAI's shape: multipart/form-data with file, purpose (user_data, assistants or vision) and optionally expires_after[seconds] (3,600 to 2,592,000; default 30 days). A PDF, a PNG, JPEG, GIF or WebP image, or UTF-8 text, at most 10 MB, typed by its bytes; 500 MB per organisation. Stored encrypted. Use it in a completion as {"type": "file", "file": {"file_id": "file-…"}}. Inference key, or a person with config:write. Off unless the deployment sets FILES_ENABLED=true: uploaded files are customer content at rest, which waits on a privacy review; until then every files route is a 403 saying so.

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Responses

200 The stored file.
id string

file-…

object enum
One of: file
bytes integer
created_at integer
expires_at integer
filename string
purpose string
mime_type string

What the bytes are, whatever the name says.

status enum
One of: processed
400 Not a supported type, an unknown purpose, or an out-of-range expiry.
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
413 Over 10 MB, or over the organisation's 500 MB.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get a file's details

GET /v1/files/{id}

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The file id (file-…).

Responses

200 The file.
id string

file-…

object enum
One of: file
bytes integer
created_at integer
expires_at integer
filename string
purpose string
mime_type string

What the bytes are, whatever the name says.

status enum
One of: processed
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Delete a file

DELETE /v1/files/{id}

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The file id.

Responses

200 Deleted.
id string
object enum
One of: file
deleted boolean
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Download a file

GET /v1/files/{id}/content

The bytes, as an attachment, to the organisation that uploaded them.

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The file id.

Responses

200 The file.
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Videos

Video generation jobs: submit, poll, download. Off unless the deployment enables it, pending a privacy review.

List videos

GET /v1/videos

The organisation's jobs, newest first, as {"object": "list", "data": [...]}. Off unless the deployment sets VIDEO_ENABLED=true: a finished video is generated content stored for 24 hours, which waits on a privacy review; until then every video route is a 403 saying so.

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Responses

200 The jobs.
id string

video_…

object enum
One of: video
model string
provider string
status enum
One of: queued, in_progress, completed, failed
created_at integer
completed_at integer | null
expires_at integer
seconds number | null
error object | null
generation_id string

The request id; GET /v1/generation?id= reads it.

usage object
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Generate a video

POST /v1/videos

OpenAI's video shape: submits a job and answers at once with a video object, status: "queued". Poll GET /v1/videos/{id} until it is completed (or failed), then download GET /v1/videos/{id}/content. Generation takes minutes.

Routed and admitted like every other call -- model, models and provider as on chat completions, the same budgets, content filters (a redacted prompt is what is generated) and guardrails -- with a hold sized at $0.60 a second of video asked for, then billed on the provider's reported cost of the generation, since video prices vary by resolution, audio and length. seconds (1 to 15) is sent in each model's own field; parameters carries the model's other fields as given (resolution, aspect ratio, negative prompt, seed...).

Off unless the deployment sets VIDEO_ENABLED=true: a finished video is generated content stored for 24 hours, which waits on a privacy review; until then every video route is a 403 saying so.

Authentication

Inference key (ar-v1-…) · Dashboard session

Parameters

X-Autonomous-Relay-Agent string · header

The agent this request is for, as on chat completions.

Request body

model string

A text-to-video model id from /v1/models.

models string[]

Fallbacks, tried in order after model.

prompt stringrequired
seconds integer
parameters object

The model's own fields, at most 30 and 8 KB; not prompt or webhook.

provider object

Routing preferences, as on chat completions.

Responses

200 The job, queued.
id string

video_…

object enum
One of: video
model string
provider string
status enum
One of: queued, in_progress, completed, failed
created_at integer
completed_at integer | null
expires_at integer
seconds number | null
error object | null
generation_id string

The request id; GET /v1/generation?id= reads it.

usage object
400 A malformed `prompt`, `seconds` or `parameters`; `model is required`; or the model is not a text-to-video model.
401 No key; an invalid, disabled or expired key; or an invalid session.
402 Not enough credit for the hold.
403 Video generation is off on this deployment; a budget is exhausted; a content filter blocked the prompt; or the key is a management key.
404 No model by that name, or no endpoint can serve it.
429 Rate limited (`rate_limited`): by the account's quota (`X-RateLimit-*`), or by the key's own `rpm_limit` or `tpm_limit` (`X-RateLimit-*-Requests`, `X-RateLimit-*-Tokens`; the message names which). `Retry-After` says when to try again.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 Every candidate refused the job.

Get a video job

GET /v1/videos/{id}

Its status -- queued, in_progress, completed or failed -- and, once known, what it cost. A job the provider has not reported on within two hours is failed and its hold given back.

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The video id (video_…).

Responses

200 The job.
id string

video_…

object enum
One of: video
model string
provider string
status enum
One of: queued, in_progress, completed, failed
created_at integer
completed_at integer | null
expires_at integer
seconds number | null
error object | null
generation_id string

The request id; GET /v1/generation?id= reads it.

usage object
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Delete a video

DELETE /v1/videos/{id}

The job and its video. A job still being generated is a 409: its result is still due.

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The video id.

Responses

200 Deleted.
id string
object enum
One of: video
deleted boolean
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
404 Not found, or not in your organization.
409 Still being generated.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Download a video

GET /v1/videos/{id}/content

The finished video, within 24 hours of its completion: the bytes as an attachment when the provider delivered them (stored encrypted), or a 302 to the provider's own link when it delivered one (the link is stored encrypted; the video stays with the provider). Follow redirects.

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The video id.

Responses

200 The video.
302 The provider's link to the video, in `Location`.
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
404 Not found, or not in your organization.
409 Not completed yet, or failed.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Receive a provider's video result

POST /v1/videos/{id}/callback

For the provider, not for you: the webhook a video job is submitted with. Authenticated by the job's secret token in the URL, compared in constant time against its hash; an unknown job or a wrong token is a bare 404. A delivery for a job already handled is acknowledged and changes nothing, so the provider's retries settle a job once.

Authentication

The token in the link we sent (query parameter token)

Parameters

id string · pathrequired

The video id.

token string · queryrequired

The job's callback secret.

Responses

200 Accepted.
ok anyrequired
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Batches

Chat requests run through the provider's own batch API, at its batch price, within 24 hours.

List batches

GET /v1/batches

The organisation's batches, newest first, as last checked.

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Responses

200 The batches.
id string

batch_…

object enum
One of: batch
endpoint enum
One of: /v1/chat/completions
model string

The :batch model.

provider string
status enum
One of: in_progress, completed, failed, cancelled, expired
created_at integer
completed_at integer | null
expires_at integer
request_counts object
errors object | null
generation_id string

The request id; GET /v1/generation?id= reads its cost.

usage object
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Create a batch

POST /v1/batches

Up to 10,000 chat-completion requests, run by the provider's own batch API -- OpenAI's Batch API, Anthropic's Message Batches, Google's Gemini batch mode, Mistral's batch jobs, Alibaba Model Studio's Batch API, xAI's Batch API -- at its batch price (the catalogue's :batch models, typically half the live price), within 24 hours, on your own provider key. Name the model (openai/gpt-5.4 or openai/gpt-5.4:batch); every request uses it.

Each request is {custom_id, body}, the body a chat completion without model or stream. The requests pass the same budgets, guardrails and content filters as live ones -- every message text is filtered, and what a filter redacts is what is submitted -- and the hold is the worst case of all of them. Server tools are refused: they run inside a live request.

Poll GET /v1/batches/{id}; once it has ended it is billed, once, on the sum of its requests' usage at the batch price, and GET /v1/batches/{id}/results streams the outcomes. Nothing of the requests or results is stored here: the provider holds them, under your account, for its own retention.

Authentication

Inference key (ar-v1-…) · Dashboard session

Parameters

X-Autonomous-Relay-Agent string · header

The agent this batch is for, as on chat completions.

Request body

model stringrequired

A chat model with a :batch row in /v1/models, named with or without the suffix.

requests object[]required
endpoint enum
One of: /v1/chat/completions
completion_window enum
One of: 24h
provider object

Routing preferences, as on chat completions.

Responses

200 The batch, submitted.
id string

batch_…

object enum
One of: batch
endpoint enum
One of: /v1/chat/completions
model string

The :batch model.

provider string
status enum
One of: in_progress, completed, failed, cancelled, expired
created_at integer
completed_at integer | null
expires_at integer
request_counts object
errors object | null
generation_id string

The request id; GET /v1/generation?id= reads its cost.

usage object
400 A malformed request, named by its index; a streamed or server-tool request; or a model with no batch row.
401 No key; an invalid, disabled or expired key; or an invalid session.
402 Not enough credit for the hold.
403 A budget is exhausted, a content filter blocked a request, or the key is a management key.
404 No model by that name, or no endpoint can serve it.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 The provider refused the batch.

Get a batch

GET /v1/batches/{id}

Its status, asking the provider when the last answer is over 30 seconds old. The first look after it has ended bills it -- once -- and usage.cost appears.

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The batch id (batch_…).

Responses

200 The batch.
id string

batch_…

object enum
One of: batch
endpoint enum
One of: /v1/chat/completions
model string

The :batch model.

provider string
status enum
One of: in_progress, completed, failed, cancelled, expired
created_at integer
completed_at integer | null
expires_at integer
request_counts object
errors object | null
generation_id string

The request id; GET /v1/generation?id= reads its cost.

usage object
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get a batch's results

GET /v1/batches/{id}/results

JSON Lines, one per request, in OpenAI's batch output shape: {custom_id, response: {status_code, body: <chat completion>}, error}, or {custom_id, response: null, error: {code, message}}. Read from the provider as it is sent; nothing is kept.

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The batch id.

Responses

200 The results.
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
404 Not found, or not in your organization.
409 The batch has not ended.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Cancel a batch

POST /v1/batches/{id}/cancel

Asks the provider to stop it. Requests already done are still billed when it ends.

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The batch id.

Responses

200 The batch.
id string

batch_…

object enum
One of: batch
endpoint enum
One of: /v1/chat/completions
model string

The :batch model.

provider string
status enum
One of: in_progress, completed, failed, cancelled, expired
created_at integer
completed_at integer | null
expires_at integer
request_counts object
errors object | null
generation_id string

The request id; GET /v1/generation?id= reads its cost.

usage object
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
404 Not found, or not in your organization.
409 It has already ended.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Classifiers

Organisation rules that tag each request from content-free signals, and may route a match through a preset.

List classifiers

GET /v1/classifiers

Organisation rules that tag each request from content-free signals, in the order they run. Any role may read this.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
id string
name string
tag string
priority integer
conditions ClassifierConditions
preset string | null
enabled boolean
created_at string
updated_at string
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Create a classifier

POST /v1/classifiers

Every enabled classifier whose conditions all hold gives the request its tag (stored on the request, shown in /v1/activity, sent back as x-autonomous-relay-tags). The first matching one that names a preset routes the request through it. Conditions read only content-free signals: the model the request named, the key, the client, the agent, the app, whether tools or images are present, streaming and the prompt's size. config:write.

Needs the Pro ($29/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Request body

name stringrequired
tag stringrequired

Lower-case letters, digits and hyphens, at most 40. Unique per organisation.

priority integer

Lower runs first; decides which matching classifier routes.

Default: 100
conditions ClassifierConditionsrequired
preset string | null

A preset slug to route a matching request through. A request naming its own @preset/… keeps it.

enabled boolean

Responses

201 OK.
id string
name string
tag string
priority integer
conditions ClassifierConditions
preset string | null
enabled boolean
created_at string
updated_at string
400 A condition that is not one, an empty rule, `no preset named …`, or `at most 50 classifiers`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 `subscription_required`: the organisation's plan does not include this. The message names the plan it needs; what was set up keeps working.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
409 A classifier with that tag exists.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Replace a classifier

PUT /v1/classifiers/{id}

Needs the Pro ($29/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The classifier id.

Request body

name stringrequired
tag stringrequired

Lower-case letters, digits and hyphens, at most 40. Unique per organisation.

priority integer

Lower runs first; decides which matching classifier routes.

Default: 100
conditions ClassifierConditionsrequired
preset string | null

A preset slug to route a matching request through. A request naming its own @preset/… keeps it.

enabled boolean

Responses

200 OK.
id string
name string
tag string
priority integer
conditions ClassifierConditions
preset string | null
enabled boolean
created_at string
updated_at string
400 As for create.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 `subscription_required`: the organisation's plan does not include this. The message names the plan it needs; what was set up keeps working.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
409 Another classifier has that tag.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Delete a classifier

DELETE /v1/classifiers/{id}

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The classifier id.

Responses

200 OK.
id string
deleted boolean
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Try a sample request

POST /v1/classifiers/evaluate

Which classifiers a request with these signals would match, and which would route it. Nothing is sent.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Request body

model string
key_name string
workspace_id string
agent string
app string
has_tools boolean
has_images boolean
stream boolean
prompt_tokens integer

Responses

200 OK.
tags string[]
route object | null
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Plugins

The plugin marketplace: MCP servers their developers host, reviewed for listing, installed per organisation and offered to the model with {"type": "autonomousrelay:plugin", "plugin": "<slug>"}.

List the plugin marketplace

GET /v1/marketplace/plugins

Public and rate limited. Every approved plugin: what it is, who publishes it, how an install authenticates (none, api_key or oauth2), its categories and logo. Not where it runs.

Authentication

None: this endpoint is public.

Responses

200 OK.
id string
slug string
name string
summary string
description string | null
publisher string
official boolean

Built by Autonomous Relay: a platform organisation owns it.

built_by string

The name to show: Autonomous Relay when official, else the publisher.

homepage_url string | null
privacy_url string | null
auth enum
One of: none, api_key, oauth2
categories string[]
icon_url string | null

The logo, on this origin, or null when it has none.

429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

One approved plugin

GET /v1/marketplace/plugins/{slug}

Its listing and, for a connector, what it does: the tools it offers (write ones wait for approval), the events it sends and what it keeps where.

Authentication

None: this endpoint is public.

Parameters

slug string · pathrequired

The plugin's slug.

Responses

200 OK.
id string
slug string
name string
summary string
description string | null
publisher string
official boolean

Built by Autonomous Relay: a platform organisation owns it.

built_by string

The name to show: Autonomous Relay when official, else the publisher.

homepage_url string | null
privacy_url string | null
auth enum
One of: none, api_key, oauth2
categories string[]
icon_url string | null

The logo, on this origin, or null when it has none.

connector object | null

What a connector says it does, from its manifest; null for a plain plugin.

404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

An approved plugin's logo

GET /v1/marketplace/plugins/{slug}/icon

Public. The image itself, under its own type, cached a day; the listing's icon_url changes when the logo does.

Authentication

None: this endpoint is public.

Parameters

slug string · pathrequired

The plugin's slug.

Responses

200 The image.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List the plugins your organisation publishes

GET /v1/plugins

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
id string
slug string
name string
summary string
description string | null
publisher string
official boolean

Built by Autonomous Relay: a platform organisation owns it.

built_by string

The name to show: Autonomous Relay when official, else the publisher.

homepage_url string | null
privacy_url string | null
auth enum
One of: none, api_key, oauth2
categories string[]
icon_url string | null

The logo, on this origin, or null when it has none.

oauth_authorize_url string | null
oauth_token_url string | null
oauth_client_id string | null
oauth_scopes string | null
has_client_secret boolean
mcp_url string
auth_header string | null
status enum
One of: private, pending, approved, rejected, suspended
review_note string | null
reviewed_at string | null
created_at string
updated_at string
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Register a plugin

POST /v1/plugins

A plugin is an MCP server you host (Streamable HTTP, public https). It starts private: your organisation can install and use it at once to build and test it; submit it for review to be listed publicly. One a super admin registers is the platform's own and is listed for every organisation at once. auth: api_key means each installing organisation pastes a key you give them, sent in auth_header (default Authorization). Agent tools -- tools that run many steps on your side and answer once -- are called like any tool, with up to 120 seconds per call. At most 20 plugins per organisation.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Request body

slug string

2-40 lowercase letters, digits or hyphens; fixed once created.

name string
summary string

At most 280 characters.

description string | null
publisher string
homepage_url string | null
privacy_url string | null
mcp_url string

Your MCP endpoint: public https, Streamable HTTP.

auth enum
One of: none, api_key, oauth2
oauth_authorize_url string

oauth2: your authorization endpoint (public https).

oauth_token_url string

oauth2: your token endpoint (public https).

oauth_client_id string

oauth2: the client id you issued us.

oauth_client_secret string

oauth2, confidential clients: write-only, sealed, never returned.

oauth_scopes string

oauth2: the scope string to request.

auth_header string

For api_key: the header the key is sent in. Default Authorization.

categories enum[]
icon string | null

The logo: data:image/png;base64,..., or JPEG, WebP or SVG, at most 64 KB decoded. An SVG may not carry scripts or event handlers. null removes it.

Responses

201 OK.
id string
slug string
name string
summary string
description string | null
publisher string
official boolean

Built by Autonomous Relay: a platform organisation owns it.

built_by string

The name to show: Autonomous Relay when official, else the publisher.

homepage_url string | null
privacy_url string | null
auth enum
One of: none, api_key, oauth2
categories string[]
icon_url string | null

The logo, on this origin, or null when it has none.

oauth_authorize_url string | null
oauth_token_url string | null
oauth_client_id string | null
oauth_scopes string | null
has_client_secret boolean
mcp_url string
auth_header string | null
status enum
One of: private, pending, approved, rejected, suspended
review_note string | null
reviewed_at string | null
created_at string
updated_at string
400 A field out of range, an unsafe URL, or `unknown field: …`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
409 The slug is taken.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Edit a plugin you publish

PATCH /v1/plugins/{id}

Any listing field but the slug. Sending publisher, mcp_url, auth, auth_header or an oauth_* field sends an approved plugin back to review -- unlisted, and unusable by other organisations, until approved again -- unless a super admin sends it. A logo or a client secret alone never does.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The plugin id.

Request body

slug string

2-40 lowercase letters, digits or hyphens; fixed once created.

name string
summary string

At most 280 characters.

description string | null
publisher string
homepage_url string | null
privacy_url string | null
mcp_url string

Your MCP endpoint: public https, Streamable HTTP.

auth enum
One of: none, api_key, oauth2
oauth_authorize_url string

oauth2: your authorization endpoint (public https).

oauth_token_url string

oauth2: your token endpoint (public https).

oauth_client_id string

oauth2: the client id you issued us.

oauth_client_secret string

oauth2, confidential clients: write-only, sealed, never returned.

oauth_scopes string

oauth2: the scope string to request.

auth_header string

For api_key: the header the key is sent in. Default Authorization.

categories enum[]
icon string | null

The logo: data:image/png;base64,..., or JPEG, WebP or SVG, at most 64 KB decoded. An SVG may not carry scripts or event handlers. null removes it.

Responses

200 OK.
id string
slug string
name string
summary string
description string | null
publisher string
official boolean

Built by Autonomous Relay: a platform organisation owns it.

built_by string

The name to show: Autonomous Relay when official, else the publisher.

homepage_url string | null
privacy_url string | null
auth enum
One of: none, api_key, oauth2
categories string[]
icon_url string | null

The logo, on this origin, or null when it has none.

oauth_authorize_url string | null
oauth_token_url string | null
oauth_client_id string | null
oauth_scopes string | null
has_client_secret boolean
mcp_url string
auth_header string | null
status enum
One of: private, pending, approved, rejected, suspended
review_note string | null
reviewed_at string | null
created_at string
updated_at string
400 A field out of range, or the slug.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Delete a plugin you publish

DELETE /v1/plugins/{id}

Every installation of it goes with it.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The plugin id.

Responses

204 Deleted.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

The logo of a plugin you publish

GET /v1/plugins/{id}/icon

Listed or not, as its icon_url in your own list points to. usage:read.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The plugin id.

Responses

200 The image.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Read the plugin's connector manifest

POST /v1/plugins/{id}/manifest

Fetches /.well-known/autonomousrelay-connector.json from the plugin's MCP origin through the SSRF guard, checks it, and stores it: capabilities, each tool's mode (a tool counts as read-only only if this and its readOnlyHint both say so) and context keys, the events it sends, its handshake_url (on its own origin, required when it sends events) and what it stores. The plugin's owner, config:write.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The plugin id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
name string
version string
capabilities string[]
tools object[]
events object[]
handshake_url string | null
uninstall_url string | null

Told when an organisation removes the install, so the connector erases what it holds for it.

data_residency object[]
400 No manifest, or one that is not valid; the message says why.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Give a connector install a new events token

POST /v1/plugin-installs/{id}/handshake

The gateway mints an events token and POSTs {install_id, events_url, events_token} to the manifest's handshake_url with the install's own credential, as tool calls carry it. Done at install; this retries after a failure or rotates the token. The old token works until the connector accepts the new one. config:write.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The install id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
id string
events_ready boolean
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 The connector did not accept it; the message says why.

Post an event from a connector

POST /v1/connectors/events

For connectors: Authorization: Bearer <events token> from the install handshake. The event must be one the manifest declares, with only its declared context keys and field names; id is the platform's own event id, and a repeat within a day answers 200 {duplicate: true}. Each active plugin_event trigger of the install and type starts a run, as its own key and inside its own caps. Nothing is stored but the id. Off unless the deployment sets HOSTED_AGENTS_ENABLED=true.

Authentication

Connector events token (bearer)

Request body

type stringrequired

e.g. order.created

id stringrequired

The platform's event id.

occurred_at string
context object
fields object

Responses

200 OK.
duplicate boolean
id string
202 OK.
id string
runs string[]
skipped object[]
400 An undeclared type, context key or field, or fields over 32 KB.
401 No events token, or one that names no enabled install.
403 Hosted agents are not enabled on this deployment.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Submit a plugin for review

POST /v1/plugins/{id}/submit

It waits in the queue for a super admin. A super admin's own submission is listed for every organisation at once, as Official.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The plugin id.

Responses

200 OK.
id string
slug string
name string
summary string
description string | null
publisher string
official boolean

Built by Autonomous Relay: a platform organisation owns it.

built_by string

The name to show: Autonomous Relay when official, else the publisher.

homepage_url string | null
privacy_url string | null
auth enum
One of: none, api_key, oauth2
categories string[]
icon_url string | null

The logo, on this origin, or null when it has none.

oauth_authorize_url string | null
oauth_token_url string | null
oauth_client_id string | null
oauth_scopes string | null
has_client_secret boolean
mcp_url string
auth_header string | null
status enum
One of: private, pending, approved, rejected, suspended
review_note string | null
reviewed_at string | null
created_at string
updated_at string
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
409 Only a private or rejected plugin can be submitted.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Install a plugin for your organisation

POST /v1/plugins/{id}/install

{id} is the plugin's slug. An approved plugin, or one your organisation publishes. For an api_key plugin, credential is the key its developer gave you: sealed at rest, sent only to the plugin, only its last four characters shown again. Then offer it in a request with {"type": "autonomousrelay:plugin", "plugin": "<slug>"} in tools. The plugin is a recipient you choose: it receives the tool arguments the model writes.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The plugin's slug.

Request body

credential string

Responses

201 OK.
id string
enabled boolean
credential_hint string | null
created_at string
events_ready_at string | null

Connectors: when the install last received its events token.

handshake_error string | null

Connectors: why the last handshake failed.

connected boolean

Whether it holds what it needs to run: an OAuth connection's tokens or a key. Always true for a plugin that takes neither.

connected_at string
connected_by object | null

Who connected it last; null when no person did, or they have left.

plugin object
400 A missing or unexpected credential.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
409 Installed already.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
503 `secret storage is not configured on this deployment`.

Start connecting an OAuth plugin

POST /v1/plugins/{id}/connect

{id} is the plugin's slug. For an oauth2 plugin: the answer's authorize_url is the plugin developer's sign-in page (authorization code + PKCE S256); send the person there. Their authorization server returns them to /v1/auth/oauth/plugin/callback, which stores the tokens on your organisation's install (sealed, refreshed before they expire) and sends them back to the dashboard. Whatever the plugin reaches upstream -- a Google account, say -- is granted to its developer there, never to us.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The plugin's slug.

Responses

200 OK.
authorize_url stringrequired
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Where a plugin's authorization server returns

GET /v1/auth/oauth/plugin/callback

Not called directly. Checks the state against the flow cookie, redeems the code with the PKCE verifier (and the plugin's client secret, if it has one), stores the tokens, and redirects to the dashboard with connected=<slug> or plugin_error=<reason>. Register {PUBLIC_ORIGIN}/v1/auth/oauth/plugin/callback as the redirect URI.

Authentication

The `state` matched to the flow cookie, and the plugin authorization server's code, redeemed with the PKCE verifier.

Responses

302 Back to the dashboard.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

An installed plugin's tools, live

GET /v1/plugins/{id}/tools

{id} is the plugin's slug. Asks the plugin now (tools/list) on your install's credential: the way to check an install works.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The plugin's slug.

Responses

200 OK.
name string
description string | null
input_schema object
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 The plugin could not be reached, refused the key, or answered with an error.

Your organisation's installed plugins

GET /v1/plugin-installs

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
id string
enabled boolean
credential_hint string | null
created_at string
events_ready_at string | null

Connectors: when the install last received its events token.

handshake_error string | null

Connectors: why the last handshake failed.

connected boolean

Whether it holds what it needs to run: an OAuth connection's tokens or a key. Always true for a plugin that takes neither.

connected_at string
connected_by object | null

Who connected it last; null when no person did, or they have left.

plugin object
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Turn an installed plugin off or on

PATCH /v1/plugin-installs/{id}

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The install id.

Request body

enabled booleanrequired

Responses

200 OK.
id string
enabled boolean
400 `unknown field: …`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Uninstall a plugin

DELETE /v1/plugin-installs/{id}

Its sealed credential goes with it.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The install id.

Responses

204 Uninstalled.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Tools

Server tools: declared in tools[] as {"type": "autonomousrelay:<name>"}, run by the gateway inside the request.

List server tools

GET /v1/tools

Every server tool in the catalogue (§11.2), whether it is available yet, whether it sends request data to a third party, and whether this organisation has it on. Any role may read this. The catalogue is the gateway's own tools; a connection's tools (autonomousrelay:plugin, autonomousrelay:mcp, and autonomousrelay:connected for all of them) are the organisation's, listed by GET /v1/tools/available.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
type string

What a request puts in tools[].type, e.g. autonomousrelay:datetime.

group enum
One of: web, multi_model, code, files, utilities
third_party boolean

Running it sends request data to a third party.

available boolean

Built. A request declaring an unavailable tool is a 400 naming it.

enabled boolean
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Turn server tools off or on

PUT /v1/tools

disabled is the full list of tools to turn off; every available tool not in it is on. A request declaring a tool that is off is refused with a 403 naming it. config:write.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Request body

disabled string[]required

Responses

200 OK.
type string

What a request puts in tools[].type, e.g. autonomousrelay:datetime.

group enum
One of: web, multi_model, code, files, utilities
third_party boolean

Running it sends request data to a third party.

available boolean

Built. A request declaring an unavailable tool is a 400 naming it.

enabled boolean
400 `not an available server tool: …`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List connected tools

GET /v1/tools/available

Every connection of the key's organisation -- its installed plugins, then its MCP servers, each by name -- with whether it works now and the tools it offers, named as the model sees them: what {"type": "autonomousrelay:connected"} would draw on. Each connection is asked live, in parallel, within 5 seconds. status is ok, not_connected (an OAuth plugin never connected), expired (its connection lapsed: reconnect it), disabled (turned off for the organisation) or unreachable (it did not answer). A plugin that could not be asked shows its stored manifest's tools (tools_source: "manifest", no descriptions); an MCP server that could not be asked shows none. mode is read only when the tool says it is read-only and, for a plugin, its manifest agrees. 30 requests a minute per key.

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Responses

200 The connections.
kind enum
One of: plugin, mcp
slug string

The plugin's slug, or the MCP server's name: what include and exclude take.

status enum
One of: ok, not_connected, expired, disabled, unreachable
tools_source enum

live: asked just now. manifest: the plugin's stored manifest, because it could not be asked.

One of: live, manifest, none
tools object[]
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List registered MCP servers

GET /v1/mcp-servers

Your organisation's own MCP servers (§17.7), whose tools a request can offer the model with {"type": "autonomousrelay:mcp", "server": "<name>"}. Never returns the credential; header_hint is its last four characters. Any role may read this.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
id string
name string
url string
allowed_tools string[] | null
header_name string | null
header_hint string | null
disabled boolean
created_at string
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Register an MCP server

POST /v1/mcp-servers

An MCP server reachable over the Streamable HTTP transport, at a public https URL. header_name and header_value are the credential it expects (e.g. Authorization: Bearer …), sealed at rest and sent only on calls to it. allowed_tools limits which of its tools may ever be offered. Owner and Admin only (config:write); at most 20.

The server receives the tool arguments the model writes, which come from your conversation: it is a recipient you choose.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Request body

name stringrequired

1–32 lowercase letters, digits or hyphens: what a request names it by.

url stringrequired

Public https; the MCP endpoint itself.

allowed_tools string[]
header_name string

A header such as Authorization or X-Api-Key; not one the transport uses.

header_value string

Its value. Sealed at rest; only the last four characters are shown again.

Responses

201 OK.
id string
name string
url string
allowed_tools string[] | null
header_name string | null
header_hint string | null
disabled boolean
created_at string
400 An unsafe URL, a reserved header, `at most 20 MCP servers`, or `unknown field: …`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
409 A server with that name exists.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
503 `secret storage is not configured on this deployment`.

Turn a registered MCP server off or on

PATCH /v1/mcp-servers/{id}

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The server id.

Request body

disabled booleanrequired

Responses

200 OK.
id string
name string
url string
allowed_tools string[] | null
header_name string | null
header_hint string | null
disabled boolean
created_at string
400 `unknown field: …`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Remove a registered MCP server

DELETE /v1/mcp-servers/{id}

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The server id.

Responses

204 Removed.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List a registered server's tools, live

GET /v1/mcp-servers/{id}/tools

Asks the server now (tools/list), narrowed to its allowed_tools: the way to check a registration works.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The server id.

Responses

200 OK.
name string
description string | null
input_schema object
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 The server could not be reached, refused the credential, or answered with an error.

Presets

Named, versioned request settings, used as "model": "@preset/<slug>".

List presets

GET /v1/presets

Each preset's current version. Any role may read this.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
slug string
name string
description string | null
version integer
body object
reference string

What a request puts in model: @preset/<slug>.

created_at string
updated_at string
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Create a preset

POST /v1/presets

A named set of request SETTINGS, used as "model": "@preset/<slug>" in a completion (or @preset/<slug>@<version> to pin one). body holds settings only — model, models, temperature, top_p, top_k, max_tokens, seed, provider, reasoning, routing_stability — never messages or a system prompt. A whole request body is accepted: other fields are dropped, named in dropped, and never stored. config:write.

Needs the Pro ($29/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Request body

slug stringrequired

Lower-case letters, digits and hyphens, at most 64.

name stringrequired
description string
body objectrequired

Responses

201 OK.
400 A field out of range, a slug that is not one, or `at most 100 presets`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 `subscription_required`: the organisation's plan does not include this. The message names the plan it needs; what was set up keeps working.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
409 A preset with that slug exists.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get a preset and its history

GET /v1/presets/{slug}

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

slug string · pathrequired

The preset slug.

Responses

200 OK.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Save a new version

PUT /v1/presets/{slug}

Every change is a new version; versions are never edited. config:write.

Needs the Pro ($29/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

slug string · pathrequired

The preset slug.

Request body

body objectrequired
note string
name string
description string | null

Responses

200 OK.
slug string
name string
description string | null
version integer
body object
reference string

What a request puts in model: @preset/<slug>.

created_at string
updated_at string
400 A field out of range.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 `subscription_required`: the organisation's plan does not include this. The message names the plan it needs; what was set up keeps working.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Delete a preset and its history

DELETE /v1/presets/{slug}

A request still naming it is then a 404. config:write.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

slug string · pathrequired

The preset slug.

Responses

200 OK.
slug string
deleted boolean
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Make an old version current again

POST /v1/presets/{slug}/restore

As a new version with the old one's body, so the history keeps what happened. config:write.

Needs the Pro ($29/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

slug string · pathrequired

The preset slug.

Request body

version integerrequired

Responses

200 OK.
slug string
name string
description string | null
version integer
body object
reference string

What a request puts in model: @preset/<slug>.

created_at string
updated_at string
400 `version must be a version number`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 `subscription_required`: the organisation's plan does not include this. The message names the plan it needs; what was set up keeps working.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Observability

Broadcast: a metadata record of each request, sent to your webhook, OpenTelemetry endpoint, S3-compatible bucket, Langfuse or Datadog.

List broadcast destinations

GET /v1/broadcast

Where a record of each completion is sent (§14.12 Observability). Never returns a signing secret or a header value; header_hint is the value's last four characters. Any role may read this.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
id string
kind enum
One of: webhook, otlp, s3, langfuse, datadog
url string
name string | null
sample_rate number
disabled boolean
header_name string | null
header_hint string | null
s3_region string

s3 only.

s3_bucket string

s3 only.

s3_prefix string | null

s3 only.

s3_path_style boolean

s3 only.

s3_access_key_id string

s3 only.

s3_secret_hint string

s3 only: the secret access key's last four characters. The key itself is never returned.

langfuse_public_key string

langfuse only: the project's public key.

langfuse_secret_hint string

langfuse only: the secret key's last four characters. The key itself is never returned.

datadog_site string

datadog only: the Datadog site, e.g. datadoghq.eu.

datadog_api_key_hint string

datadog only: the API key's last four characters. The key itself is never returned.

last_attempt_at string | null
last_success_at string | null
last_http_status integer | null
last_error string | null
created_at string
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Add a broadcast destination

POST /v1/broadcast

After every completion, a record of the request is sent to each destination — metadata only: model, provider, status, tokens, cost, timing, key name, client, agent, session and run; never the prompt or the completion.

webhook: a JSON POST {"type":"request.completed","request":{…}}, signed with X-AgentsRouter-Signature (t=…,v1=hmac_sha256(secret, t + "." + body)); the signing secret is returned once, here. otlp: one OpenTelemetry span per request (OTLP/HTTP JSON, gen_ai.* attributes) to the URL given, which is the full traces endpoint, e.g. https://api.honeycomb.io/v1/traces; header_name and header_value carry the vendor's API key, sealed at rest and never shown again. s3: S3-compatible object storage (AWS S3, Cloudflare R2, Backblaze B2, MinIO, Google Cloud Storage with HMAC keys). url is the endpoint without a bucket, e.g. https://s3.us-east-1.amazonaws.com; records are batched — up to 500 records, 4 MB, or 60 seconds — and each batch is one newline-delimited JSON object (each line the webhook's request.completed record) at {s3_prefix}{YYYY}/{MM}/{DD}/{HH}/{timestamp}-{random}.jsonl, UTC, signed with AWS Signature Version 4. The key needs only s3:PutObject on the bucket; s3_secret_access_key is sealed at rest and never shown again.

langfuse: each request becomes a Langfuse trace with one generation (model, token usage, cost, latency, time to first token, level), sent over OTLP/HTTP JSON to {url}/api/public/otel/v1/traces with Basic auth from langfuse_public_key and langfuse_secret_key; batched up to 100 records or 10 seconds. datadog: each request becomes a log (service:autonomousrelay, ddsource:autonomousrelay, ddtags model:…,provider:…,status:…, the request record as attributes) sent to https://http-intake.logs.{datadog_site}/api/v2/logs with DD-API-KEY; batched up to 500 records or 10 seconds. Both carry metadata only — no prompt or completion.

The URL must be public https. At most 10 destinations; sample_rate sends that fraction of requests. Delivery is after the response, never on the request path. A transient failure (a network error, HTTP 429 or 5xx) is retried up to 3 attempts in all with jittered exponential backoff; any other refusal is final. The final outcome is recorded on the destination (last_error).

Authentication

Management key (ar-mgmt-…) · Dashboard session

Request body

kind enumrequired
One of: webhook, otlp, s3, langfuse, datadog
url stringrequired

Public https. For s3, the storage endpoint without a bucket or path. For langfuse, the Langfuse host (default https://cloud.langfuse.com; e.g. https://us.cloud.langfuse.com or a self-hosted URL). Not for datadog, whose intake is derived from datadog_site.

name string
header_name string

webhook and otlp: an HTTP header to send, e.g. x-honeycomb-team or Authorization.

header_value string

Its value. Sealed at rest; only the last four characters are shown again.

sample_rate number
Default: 1
s3_region string

s3, required: the signing region, e.g. us-east-1; auto for Cloudflare R2.

s3_bucket string

s3, required: the bucket name.

s3_prefix string

s3: prepended to every object key, e.g. relay/prod/. At most 256 characters.

s3_path_style boolean

s3: https://endpoint/bucket/key rather than https://bucket.endpoint/key. Defaults to false for *.amazonaws.com, true elsewhere; a bucket with dots needs true.

s3_access_key_id string

s3, required.

s3_secret_access_key string

s3, required. Sealed at rest; only the last four characters are shown again.

langfuse_public_key string

langfuse, required: the project's public key, pk-lf-….

langfuse_secret_key string

langfuse, required: the project's secret key, sk-lf-…. Sealed at rest; only the last four characters are shown again.

datadog_site enum

datadog: the Datadog site of the organisation.

One of: datadoghq.com, us3.datadoghq.com, us5.datadoghq.com, datadoghq.eu, ap1.datadoghq.com, ap2.datadoghq.com, uk1.datadoghq.com, ddog-gov.com, us2.ddog-gov.com
Default: "datadoghq.com"
datadog_api_key string

datadog, required: a Datadog API key (32 hexadecimal characters). Sealed at rest; only the last four characters are shown again.

Responses

201 OK.
400 An unsafe URL, a reserved header, an incomplete or invalid `s3_*`, `langfuse_*` or `datadog_*` field, `at most 10 destinations`, or `unknown field: …`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
503 `secret storage is not configured on this deployment`.

Pause, resume, rename or resample a destination

PATCH /v1/broadcast/{id}

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The destination id.

Request body

disabled boolean
sample_rate number
name string | null

Responses

200 OK.
id string
kind enum
One of: webhook, otlp, s3, langfuse, datadog
url string
name string | null
sample_rate number
disabled boolean
header_name string | null
header_hint string | null
s3_region string

s3 only.

s3_bucket string

s3 only.

s3_prefix string | null

s3 only.

s3_path_style boolean

s3 only.

s3_access_key_id string

s3 only.

s3_secret_hint string

s3 only: the secret access key's last four characters. The key itself is never returned.

langfuse_public_key string

langfuse only: the project's public key.

langfuse_secret_hint string

langfuse only: the secret key's last four characters. The key itself is never returned.

datadog_site string

datadog only: the Datadog site, e.g. datadoghq.eu.

datadog_api_key_hint string

datadog only: the API key's last four characters. The key itself is never returned.

last_attempt_at string | null
last_success_at string | null
last_http_status integer | null
last_error string | null
created_at string
400 A field out of range, or `unknown field: …`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Remove a destination

DELETE /v1/broadcast/{id}

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The destination id.

Responses

200 OK.
id string
deleted boolean
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Send a test record now

POST /v1/broadcast/{id}/test

A synthetic request record, marked as a test, delivered at once; the answer says how the receiver responded. Sent once, without retries. For s3, langfuse and datadog, a batch of that one record is sent straight away, not buffered.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The destination id.

Responses

200 OK.
delivered boolean
http_status integer | null
error string | null
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Workspaces

One per client: separate keys, budgets and spend under one account.

List workspaces

GET /v1/workspaces

One workspace per client is the intended use: each has its own keys, budgets and spend, under one account and one balance. spend_this_month is measured the way a workspace budget is -- settled spend in the current UTC calendar month plus requests in flight, BYOK spend at list price included.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
id string
name string
slug string
description string | null

Up to 500 characters.

your_role string | null

The caller's workspace role when their seat is limited to workspaces; null for a seat that sees the whole organisation.

created_at string
active_keys integer
spend_this_month number

USD, current UTC calendar month, measured as a workspace budget measures it.

budgets object[]
is_current boolean

Whether this is the calling key's own workspace.

401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Create a workspace

POST /v1/workspaces

At most 200 per organisation. Put keys in it with workspace_id on POST /v1/keys, or move one with PATCH /v1/keys/{hash}.

Needs the Team ($99/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

name stringrequired

Up to 80 characters, e.g. the client's name.

description string

Optional, up to 500 characters.

Responses

201 Created.
id string
name string
slug string
description string | null

Up to 500 characters.

your_role string | null

The caller's workspace role when their seat is limited to workspaces; null for a seat that sees the whole organisation.

created_at string
active_keys integer
spend_this_month number

USD, current UTC calendar month, measured as a workspace budget measures it.

budgets object[]
is_current boolean

Whether this is the calling key's own workspace.

400 `name is required`, too long, or the 200-workspace cap.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 `subscription_required`: the organisation's plan does not include this. The message names the plan it needs; what was set up keeps working.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Rename or describe a workspace

PATCH /v1/workspaces/{id}

Send name, description (null clears it) or both. A seat limited to workspaces needs to be an admin of this one.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The workspace id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

name string
description string | null

Responses

200 OK.
id string
name string
slug string
description string | null

Up to 500 characters.

your_role string | null

The caller's workspace role when their seat is limited to workspaces; null for a seat that sees the whole organisation.

created_at string
active_keys integer
spend_this_month number

USD, current UTC calendar month, measured as a workspace budget measures it.

budgets object[]
is_current boolean

Whether this is the calling key's own workspace.

401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Remove a workspace

DELETE /v1/workspaces/{id}

Refused with 409 while the workspace has an active key, and for the organisation's last workspace. Revoked keys keep their history and lose the link; provider credentials are never removed with a workspace.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The workspace id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
deleted anyrequired
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
409 The workspace has active keys, or it is the last one.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List a workspace's budgets

GET /v1/workspaces/{id}/budgets

An unknown workspace is an empty list, not a 404.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

A workspace id.

Responses

200 OK.
interval enum
One of: daily, weekly, monthly, lifetime
limit_nanodollars string

Nanodollars (1e-9 USD), an integer as a string.

include_byok boolean

Count spend made on your own provider credential toward this ceiling, at IMPUTED LIST PRICE -- what the call would have cost on platform capacity, not the platform fee charged. Defaults to true: every model is BYOK-only, so a ceiling that skipped this spend would count nothing and could never bind. Set false to count only platform spend.

401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Set a workspace budget

PUT /v1/workspaces/{id}/budgets/{interval}

Budgets must shrink as the interval narrows: a daily budget above the weekly one is refused. Each interval is its own ceiling; they are never summed.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

A workspace id.

interval enum · pathrequired

The budget's period.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

limit_usd numberrequired
include_byok boolean

Responses

200 OK.
workspace_id string
interval string
limit_usd number

US dollars.

400 An unknown interval, a negative limit, or a budget larger than a longer interval's.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Delete a workspace budget

DELETE /v1/workspaces/{id}/budgets/{interval}

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

A workspace id.

interval string · pathrequired

The budget's period.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
deleted anyrequired
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List a workspace's members

GET /v1/workspaces/{id}/members

The people given a role in this workspace. Membership matters only to seats limited to workspaces (scope: "workspaces" on PATCH /v1/members/{userId}): everyone else sees every workspace.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The workspace id.

Responses

200 OK.
user_id string
role enum
One of: admin, member
created_at string
name string | null
email string | null
org_role enum

owner = Owner, member = Developer, billing = Finance. What each may do: see the Members tag.

One of: owner, admin, member, billing, readonly
scope enum

workspaces: limited to the workspaces this person is a member of.

One of: org, workspaces
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Add a workspace member or change their role

PUT /v1/workspaces/{id}/members/{userId}

A workspace admin can rename it, set its budgets and manage its members; a member can use it (keys, logs, reports). Allowed for an owner or admin of the organisation, or an admin of this workspace. The person must have an active seat.

Needs the Team ($99/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The workspace id.

userId string · pathrequired

The member's user id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

role enumrequired
One of: admin, member

Responses

200 OK.
workspace_id string
user_id string
role string
400 An unknown role.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 `subscription_required`: the organisation's plan does not include this. The message names the plan it needs; what was set up keeps working.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Remove a workspace member

DELETE /v1/workspaces/{id}/members/{userId}

A seat limited to workspaces loses this one at once; with none left, it sees nothing.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The workspace id.

userId string · pathrequired

The member's user id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
removed boolean
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Status

Measured service status.

Measured uptime, last 90 days

GET /v1/status/uptime

From an external probe, not from traffic: a Cloud Monitoring uptime check requests /health (which queries the database) every minute from four regions. One entry per UTC day, oldest first. A day with fewer than 60 probes is uptime: null -- unmeasured, never counted as up. overall is over measured days, weighted by probes, and null when none is measured. Public, limited per client address, cached for a minute.

Authentication

None: this endpoint is public.

Responses

200 The last 90 days.
days object[]
overall number | null

0 to 1, over measured days.

measured_days integer
429 Too many requests from this address.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

The incident log, last 90 days

GET /v1/status/incidents

Incidents posted by the operator, in English and French: those started in the last 90 days and any still open, newest first, each with its updates oldest first. status is the latest update's; resolved_at is set by the update that resolved it. Written by people, not inferred from the uptime probe. subscriptions.email says whether incident emails can be requested on this deployment. Public, limited per client address, cached for a minute.

Authentication

None: this endpoint is public.

Responses

200 The incident log.
id string
title_en string
title_fr string
impact enum
One of: minor, major, critical
status enum
One of: investigating, identified, monitoring, resolved
started_at string
resolved_at string | null
updates object[]
429 Too many requests from this address.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

The incident log as an Atom feed

GET /v1/status/incidents.atom

RFC 4287, one entry per update, newest first, over the same incidents as /v1/status/incidents. Follow the status page without giving an email address. Public and cached for a minute.

Authentication

None: this endpoint is public.

Parameters

lang enum · query

en (default) or fr.

Responses

200 The feed.
429 Too many requests from this address.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Ask to be emailed about incidents

POST /v1/status/subscription

Public; no account. Double opt-in: the address is sent one confirmation email, and nothing else until its link is clicked; an unconfirmed request is deleted after 7 days. Once confirmed, every incident and update posted afterwards is emailed once, in the chosen language, each with a one-click unsubscribe link. The answer is the same 202 whether or not an email was sent, so it says nothing about any address. Rate limited per client address and per email address.

Authentication

None: this endpoint is public.

Request body

email stringrequired

The address to email.

lang enum

The language of the emails. Default en.

One of: en, fr

Responses

202 Check your inbox.
status enumrequired
One of: check_inbox
message stringrequired
400 `email` is not an email address.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
503 Email is not configured on this deployment (`service_unavailable`).

Confirm an incident email subscription

GET /v1/status/subscription/confirm

The link in the confirmation email. Valid for 7 days, and idempotent while valid. Redirects to the status page with subscription=confirmed, or subscription=invalid for an unknown or expired link.

Authentication

The token in the link we sent (query parameter token)

Parameters

token string · queryrequired

From the confirmation email.

Responses

303 To the status page, with the outcome.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Unsubscribe from incident emails (link)

GET /v1/status/subscription/unsubscribe

The link in every incident email; no sign-in. Deletes the subscription and the address. Redirects to the status page with subscription=unsubscribed, or subscription=invalid for a link already used or unknown. Never refused by region: it only deletes.

Authentication

The token in the link we sent (query parameter token)

Parameters

token string · queryrequired

From the email.

Responses

303 To the status page, with the outcome.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Unsubscribe from incident emails (one-click, RFC 8058)

POST /v1/status/subscription/unsubscribe

What a mail client POSTs to the List-Unsubscribe URL, with the body List-Unsubscribe=One-Click (form-encoded; ignored). The same deletion as the link.

Authentication

The token in the link we sent (query parameter token)

Parameters

token string · queryrequired

From the email's List-Unsubscribe header.

Responses

200 OK.
unsubscribed booleanrequired
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Reports

White-label client reports: the agency's branding, and a client's month as numbers.

Report branding

GET /v1/branding

The name, logo and footer printed on client reports in place of ours. Any role may read it.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
org_name string

The organisation's own name; the fallback when brand_name is unset.

brand_name string | null
brand_logo string | null

A PNG or JPEG data URI.

brand_footer string | null
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Set report branding

PUT /v1/branding

Owner or Developer. Every field is optional; an absent or empty one clears it. The logo is a PNG or JPEG data URI under 150 KB, checked by its bytes: nothing is ever fetched from a URL, and SVG is refused.

Needs the Team ($99/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

brand_name string | null

Up to 80 characters.

brand_logo string | null

data:image/png;base64,... or data:image/jpeg;base64,..., under 150 KB.

brand_footer string | null

Up to 200 characters, e.g. a contact line.

Responses

200 OK.
org_name string

The organisation's own name; the fallback when brand_name is unset.

brand_name string | null
brand_logo string | null

A PNG or JPEG data URI.

brand_footer string | null
400 A field too long, or a logo that is not a small PNG or JPEG.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 `subscription_required`: the organisation's plan does not include this. The message names the plan it needs; what was set up keeps working.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

A client's month, as a report

GET /v1/workspaces/{id}/report

Totals, by model, by agent and by day for one workspace in a UTC calendar month, with the branding to print it under. Amounts are list price -- what the tokens were worth, the number a client is re-billed on and the one budgets measure -- and the same measurement as the CSV export. A usage report, not an invoice: no tax, due date or markup.

Needs the Team ($99/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The workspace id.

month string · query

YYYY-MM, UTC. Default: the current month.

Responses

200 OK.
workspace object
month string
period object
branding Branding
totals ReportLine
by_model ReportLine[]
by_agent ReportLine[]
by_day ReportLine[]
generated_at string
400 `month must be YYYY-MM`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 `subscription_required`: the organisation's plan does not include this. The message names the plan it needs; what was set up keeps working.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Audit

The organisation's audit log: who changed what, and when. Owner or Admin.

List the organisation's audit log

GET /v1/audit-log

Who changed what, and when: keys created, revoked or re-limited; provider keys; guardrails; members invited, re-roled or removed; SSO, SCIM, broadcast, plugins, workspaces, alert channels and organisation settings. Newest first, keyset-paginated. Owner or Admin (a key with no person behind it has full power). The actor is a user id, or for a key with no person behind it the first 12 characters of the key's hash; details holds non-secret facts only -- never a key, a secret, an address or prompt content. Kept for one year.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

limit integer · query

Page size.

before string · query

The next_cursor of the previous page. Opaque; do not parse it.

action string · query

One action, e.g. api_key.revoked, or a category, e.g. api_key.

actor string · query

A user id, or a key reference as the log shows it.

Responses

200 A page of events.
id string
at string
actor object
action string
target object | null
details object
400 A malformed `limit`, `before`, `action` or `actor`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Members

Team seats. Roles: Owner (everything, including members), Developer (keys, provider keys, limits, workspaces, alerts, agents; reads billing), Finance (usage, exports, credits and checkout; changes nothing else). A management key with no person behind it has full power. Every management route shares one rate limit per key: 300 requests a minute, 20,000 a day (429 past it).

Get the organisation

GET /v1/org

Its id, name, slug, plan, creation date and number of active seats. Any role may read this.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
id stringrequired
name stringrequired
slug stringrequired
plan stringrequired
created_at stringrequired
members integerrequired

Active seats.

401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Rename the organisation

PATCH /v1/org

Owner and Admin only (org:write). The name is what invitations and client reports show; the slug and id do not change. Trimmed, 1 to 80 characters, no control characters.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Request body

name stringrequired

Responses

200 OK.
id stringrequired
name stringrequired
slug stringrequired
plan stringrequired
created_at stringrequired
members integerrequired

Active seats.

400 `name must not be empty`, `name must be at most 80 characters`, or `name must not contain control characters`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List members

GET /v1/members

Everyone with an active seat in the organisation, owners first, and the caller's own role. Any role may read this, a seat limited to workspaces included.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 The members.
user_id string
role enum

owner = Owner, member = Developer, billing = Finance. What each may do: see the Members tag.

One of: owner, admin, member, billing, readonly
role_name string
scope enum

workspaces: limited to the workspaces this person is a member of.

One of: org, workspaces
joined_at string
name string | null
email string | null
avatar_url string | null
provider string | null
is_you boolean
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Change a member's role or scope

PATCH /v1/members/{userId}

Owners only. Send role, scope or both. scope: "workspaces" limits a Developer or Read-only seat to the workspaces they are a member of (PUT /v1/workspaces/{id}/members/{userId}); with no memberships they see nothing, never the whole organisation. Owners, admins and Finance always see everything, so moving a seat to one of those roles sets its scope back to org. The organisation's last owner cannot be demoted (409).

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

userId string · pathrequired

The member's user id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

role enum
One of: owner, member, billing
scope enum

workspaces: limited to the workspaces this person is a member of.

One of: org, workspaces

Responses

200 OK.
user_id string
role string
scope enum

workspaces: limited to the workspaces this person is a member of.

One of: org, workspaces
400 An unknown role or scope, neither sent, or `workspaces` on a role that cannot be limited.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
409 The last owner.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Remove a member

DELETE /v1/members/{userId}

Owners only. The seat is deactivated and the person's dashboard key in this organisation is revoked, ending their sessions here at once; their other organisations are untouched. The last owner cannot be removed (409).

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

userId string · pathrequired

The member's user id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
removed boolean
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
409 The last owner.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Open invitations

GET /v1/invitations

Owners only. Invitations not yet accepted, revoked or expired. Links are not listed: only their hashes are kept.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
id string
role string
created_at string
expires_at string
created_by_name string | null
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Invite someone

POST /v1/invitations

Owners only. Returns a link, in this response and never again: single use, valid seven days, revocable. The invitee opens it, signs in, and accepts. At most 50 open invitations.

Needs the Team ($99/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

role enumrequired
One of: owner, member, billing

Responses

201 Created.
id string
role string
created_at string
expires_at string
url string

The invitation link. Store or send it now.

401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 `subscription_required`: the organisation's plan does not include this. The message names the plan it needs; what was set up keeps working.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Revoke an invitation

DELETE /v1/invitations/{id}

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The invitation id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
revoked boolean
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

What an invitation is for

POST /v1/invitations/preview

Public, limited per address. The token travels in the body to stay out of access logs. An unknown, used, revoked or expired token all answer the same 404.

Authentication

The invitation token, in the body (kept out of URLs and access logs).

Request body

token stringrequired

Responses

200 OK.
org_name string
role string
role_name string
expires_at string
404 Not found, or not in your organization.
429 Too many requests from this address.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Accept an invitation

POST /v1/invitations/accept

Requires a signed-in PERSON (a dashboard session); an API key has no seat and gets 403. Takes the seat, and replaces the session with one in the inviting organisation (Set-Cookie).

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

token stringrequired

Responses

200 OK.
org_id string
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Agents

Agents named in X-Autonomous-Relay-Agent: their spend, and budgets that bind across every key they use.

List agents

GET /v1/agents

Every agent named in X-Autonomous-Relay-Agent in the last 31 days, plus any with a budget but no traffic yet: requests and spend this UTC month (list price included, measured as an agent budget measures it) and budgets. Newest first; at most 500.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
agent string
last_seen string | null
requests_this_month integer
spend_this_month number

US dollars.

budgets object[]
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List hosted agents

GET /v1/agents/definitions

Each saved agent's current version: model, tools and run caps. Never the instructions. Any role may read this.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
slug string

Also the X-Autonomous-Relay-Agent tag every turn carries, so agent budgets apply.

name string
description string | null
status enum
One of: active, paused, archived
version integer
model string

A model id or @preset/<slug>.

tools object[]
run_defaults object
approval object
output object
invoke string

The path that runs it.

created_at string
updated_at string
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Create a hosted agent

POST /v1/agents/definitions

A saved agent: its instructions, the model or preset it runs on, the tools the gateway may run for it, and the caps every invocation opens a run under (budget_usd, max_turns and max_tool_calls are required). Run it with POST /v1/agents/{slug}/invoke. config:write. Off unless the deployment sets HOSTED_AGENTS_ENABLED=true: storing agent instructions waits on a privacy review, and until then this is a 403 saying so.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

instructions stringrequired

The system prompt, at most 32 KB. Sealed at rest; returned only to config:write.

model stringrequired

A model id or @preset/<slug>.

tools object[]

Up to 8, each one the gateway runs: autonomousrelay:plugin (plugin, optional allowed_tools), autonomousrelay:mcp (server, optional allowed_tools), or a built server tool. A plain function is refused: nobody is standing by to run it.

run_defaults objectrequired
approval object
output object
slug stringrequired

Lower-case letters, digits and hyphens, at most 64.

name stringrequired
description string

Responses

201 OK.
slug string

Also the X-Autonomous-Relay-Agent tag every turn carries, so agent budgets apply.

name string
description string | null
status enum
One of: active, paused, archived
version integer
model string

A model id or @preset/<slug>.

tools object[]
run_defaults object
approval object
output object
invoke string

The path that runs it.

created_at string
updated_at string
400 A field out of range, a tool the gateway cannot run, a missing cap, or `at most 100 agents`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
409 An agent with that slug exists.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get a hosted agent and its history

GET /v1/agents/definitions/{slug}

instructions is the current version's, and only for config:write; for any other role it is null.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

slug string · pathrequired

The agent slug.

Responses

200 OK.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Save a new version

PUT /v1/agents/definitions/{slug}

The whole body again; every change is a new version and versions are never edited, so a run always names what it ran under. config:write. Off unless the deployment sets HOSTED_AGENTS_ENABLED=true: storing agent instructions waits on a privacy review, and until then this is a 403 saying so.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

slug string · pathrequired

The agent slug.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

instructions stringrequired

The system prompt, at most 32 KB. Sealed at rest; returned only to config:write.

model stringrequired

A model id or @preset/<slug>.

tools object[]

Up to 8, each one the gateway runs: autonomousrelay:plugin (plugin, optional allowed_tools), autonomousrelay:mcp (server, optional allowed_tools), or a built server tool. A plain function is refused: nobody is standing by to run it.

run_defaults objectrequired
approval object
output object
name string
description string
note string

At most 200 characters.

Responses

200 OK.
slug string

Also the X-Autonomous-Relay-Agent tag every turn carries, so agent budgets apply.

name string
description string | null
status enum
One of: active, paused, archived
version integer
model string

A model id or @preset/<slug>.

tools object[]
run_defaults object
approval object
output object
invoke string

The path that runs it.

created_at string
updated_at string
400 A field out of range, or `at most 200 versions`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Pause, resume or archive a hosted agent

PATCH /v1/agents/definitions/{slug}

A paused or archived agent refuses invocations with 409; its runs and versions stay. config:write.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

slug string · pathrequired

The agent slug.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

status enumrequired
One of: active, paused, archived

Responses

200 OK.
slug string

Also the X-Autonomous-Relay-Agent tag every turn carries, so agent budgets apply.

name string
description string | null
status enum
One of: active, paused, archived
version integer
model string

A model id or @preset/<slug>.

tools object[]
run_defaults object
approval object
output object
invoke string

The path that runs it.

created_at string
updated_at string
400 Not a status, or a field other than status.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Delete a hosted agent

DELETE /v1/agents/definitions/{slug}

Deletes every version of its instructions and its triggers, and erases the paused state of any of its runs waiting on an approval, cancelling those runs. Its runs keep their record, without a link to the agent. config:write.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

slug string · pathrequired

The agent slug.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
slug string
deleted boolean
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Run a hosted agent

POST /v1/agents/{slug}/invoke

Opens a run under the agent's caps (an overrides value may lower budget_usd, max_turns or max_tool_calls, never raise one), sends its instructions and your input through POST /v1/chat/completions with its tools and X-Autonomous-Relay-Agent set to the slug, lets the gateway run the tool loop, closes the run and returns the answer. Everything a completion gets applies: presets, guardrails, routing, billing, agent budgets. With stream: true the answer streams as POST /v1/chat/completions does, between two SSE comments of the run's: : autonomousrelay {"type": "run.opened", ...} first and {"type": "run.closed", "run": {...}} just before [DONE]. Nothing is stored but the run's metadata. Off unless the deployment sets HOSTED_AGENTS_ENABLED=true: storing agent instructions waits on a privacy review, and until then this is a 403 saying so.

Authentication

Inference key (ar-v1-…)

Parameters

slug string · pathrequired

The agent slug.

Request body

input stringrequired

What the agent is asked to do, at most 100,000 characters.

overrides object
stream boolean
Default: false

Responses

200 OK.
agent string
version integer
status enum
One of: completed, awaiting_approval
approval object
decision enum

On a decision's answer only.

One of: approved, rejected
output string | null
finish_reason string | null
model string | null
generation_id string | null
usage object
run object
202 The run paused before a write: `status` is `awaiting_approval` and `approval` names the pending action. It continues when a person decides it.
agent string
version integer
status enum
One of: completed, awaiting_approval
approval object
decision enum

On a decision's answer only.

One of: approved, rejected
output string | null
finish_reason string | null
model string | null
generation_id string | null
usage object
run object
400 No input, an unknown field, or an override above the definition's cap.
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
404 Not found, or not in your organization.
409 The agent is paused or archived.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
502 The agent's run failed upstream; nothing was billed beyond what ran.

List a hosted agent's triggers

GET /v1/agents/definitions/{slug}/triggers

Its webhooks and schedules. Never a signing secret. Any role may read this.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

slug string · pathrequired

The agent slug.

Responses

200 OK.
id string
kind enum
One of: webhook, schedule, plugin_event
name string
plugin string

plugin_event: the installed plugin's slug.

event_type string

plugin_event: the event its connector posts.

key_hash string

The inference key the runs are made as.

input_template string

The run's input: {{path}} placeholders filled from the delivery ({{body}} for all of it); on a schedule, from fired_at and trigger.

event_id_field string | null

Webhook: where in the delivery the sender's event id is; a repeat within a day starts nothing.

every_minutes integer | null

Schedule: every this many minutes, 5 to 10080.

next_fire_at string | null
max_runs_per_hour integer
max_concurrent integer
status enum
One of: active, paused
last_fired_at string | null
url string

Webhook: the path deliveries are posted to.

created_at string
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Start a hosted agent from a webhook or a schedule

POST /v1/agents/definitions/{slug}/triggers

A webhook starts a run for each signed delivery to its url; a schedule every every_minutes. Runs are made as the inference key key_hash names -- billed, capped and tagged as that key's, and stopped by revoking or pausing it -- within the agent's run caps and the trigger's own max_runs_per_hour (default 60) and max_concurrent (default 3). A webhook's signing_secret is returned once, here. config:write. Off unless the deployment sets HOSTED_AGENTS_ENABLED=true.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

slug string · pathrequired

The agent slug.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

kind enumrequired
One of: webhook, schedule, plugin_event
name stringrequired
key_hash stringrequired
plugin string

plugin_event: the installed plugin's slug.

event_type string

plugin_event: an event the plugin's manifest declares, e.g. order.created.

input_template stringrequired

At most 4000 characters.

every_minutes integer
event_id_field string

A field path, e.g. id or data.event_id.

max_runs_per_hour integer
Default: 60
max_concurrent integer
Default: 3

Responses

201 OK.
400 A field out of range, a field the kind cannot use, a key that is not an active inference key of this organisation, or `at most 20 triggers per agent`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Pause or resume a trigger

PATCH /v1/agents/triggers/{id}

Only status; delete and recreate a trigger to change anything else. config:write.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The trigger id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

status enumrequired
One of: active, paused

Responses

200 OK.
id string
kind enum
One of: webhook, schedule, plugin_event
name string
plugin string

plugin_event: the installed plugin's slug.

event_type string

plugin_event: the event its connector posts.

key_hash string

The inference key the runs are made as.

input_template string

The run's input: {{path}} placeholders filled from the delivery ({{body}} for all of it); on a schedule, from fired_at and trigger.

event_id_field string | null

Webhook: where in the delivery the sender's event id is; a repeat within a day starts nothing.

every_minutes integer | null

Schedule: every this many minutes, 5 to 10080.

next_fire_at string | null
max_runs_per_hour integer
max_concurrent integer
status enum
One of: active, paused
last_fired_at string | null
url string

Webhook: the path deliveries are posted to.

created_at string
400 Not a status, or another field.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Delete a trigger

DELETE /v1/agents/triggers/{id}

Its runs keep their record. config:write.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The trigger id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
id string
deleted boolean
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Deliver an event to a webhook trigger

POST /v1/agents/hooks/{id}

Authenticated by X-Autonomous-Relay-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>"> under the trigger's signing secret, valid for five minutes -- the form the gateway's own webhooks carry. The body must be a JSON object, at most 256 KB; it is rendered into the run's input and not stored. Answers 202 with the run's id at once; the run goes on in the background and is on the Runs page. An unknown, paused or badly signed delivery is the same 401.

Authentication

Signature header X-Autonomous-Relay-Signature

Parameters

id string · pathrequired

The trigger id.

Responses

200 OK.
duplicate boolean
event_id string
202 OK.
run_id string
400 Not a JSON object.
401 Unknown trigger, paused trigger, or a missing, stale or wrong signature.
403 Hosted agents are not enabled on this deployment.
409 The agent is not active, or the trigger's key is revoked, expired or paused.
429 The trigger is at its max_runs_per_hour or max_concurrent.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List hosted agents' pending actions

GET /v1/agents/approvals

Actions hosted agents proposed and that wait for, or got, a person's decision. Names only; the arguments are on the action itself, for config:write. Newest first; at most 200. Any role may read this.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

status enum · query

pending (default), approved, rejected or expired.

Responses

200 OK.
id string
run_id string
agent string | null
tools object[]
status enum
One of: pending, approved, rejected, expired
note string | null
decided_by string | null
decided_at string | null
created_at string
expires_at string
decide string

The path that decides it.

400 Not a status.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get a pending action

GET /v1/runs/{id}/actions/{action}

calls holds the proposed calls with their arguments while the action is pending, and only for config:write; otherwise null. The arguments are erased when it is decided or expires.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The run id.

action string · pathrequired

The action id.

Responses

200 OK.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Approve or reject a pending action

POST /v1/runs/{id}/actions/{action}

Approving runs the proposed calls; rejecting tells the model they were refused (with your note, if any). Either way the run resumes and the conversation continues as the key that invoked the agent -- billed, capped and tagged as any of its turns -- and the answer is what invoke answers, with decision. The held state is erased in the same write that records the decision. config:write. Off unless the deployment sets HOSTED_AGENTS_ENABLED=true.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The run id.

action string · pathrequired

The action id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

decision enumrequired
One of: approve, reject
note string

At most 500 characters. On a rejection, the model is told it.

Responses

200 OK.
agent string
version integer
status enum
One of: completed, awaiting_approval
approval object
decision enum

On a decision's answer only.

One of: approved, rejected
output string | null
finish_reason string | null
model string | null
generation_id string | null
usage object
run object
202 The continuation proposed another write: decide that one next.
agent string
version integer
status enum
One of: completed, awaiting_approval
approval object
decision enum

On a decision's answer only.

One of: approved, rejected
output string | null
finish_reason string | null
model string | null
generation_id string | null
usage object
run object
400 Not a decision, or a note out of range.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
409 The action was already decided, has expired (its run has ended), or its run is no longer waiting.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Set an agent budget

PUT /v1/agents/{agent}/budgets/{interval}

A hard ceiling on one agent's spend in a window, across every key it uses: a request naming the agent is refused with 403 budget_exceeded once the agent is at or past it. Narrower windows must have smaller caps. BYOK spend counts at list price by default.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

agent string · pathrequired

The X-Autonomous-Relay-Agent value.

interval string · pathrequired

daily, weekly, monthly or lifetime.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

limit_usd numberrequired
include_byok boolean
Default: true
mode enum

hard refuses at the ceiling (403 budget_exceeded); soft serves, adds the sentence to x-autonomous-relay-limit-warning, and still alerts.

One of: hard, soft
Default: "hard"
workspace_id string | null

Scope the budget to this client workspace: it binds only requests from that workspace's keys. Omitted or null: the whole organisation. Both may exist for one agent and interval.

Responses

200 OK.
agent string
interval string
limit_usd number
include_byok boolean
mode enum
One of: hard, soft
workspace_id string | null
400 A bad agent, interval or amount, or a budget above a wider window's.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Remove an agent budget

DELETE /v1/agents/{agent}/budgets/{interval}

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

agent string · pathrequired

The X-Autonomous-Relay-Agent value.

interval string · pathrequired

The window.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

workspace_id string · query

Remove the budget scoped to this client workspace. Absent: the organisation-wide one.

Responses

200 OK.
deleted anyrequired
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Guardrails

Policy: allowlists, budgets, privacy and content filters.

Get the account routing defaults

GET /v1/routing

The default model, fallback models and default provider sort for every key in the organisation. Any role may read this.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
default_model string | nullrequired
fallback_models string[]required
default_sort enumrequired
One of: price, throughput, latency, null
lock_sort booleanrequired
updated_at string | nullrequired
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Set the account routing defaults

PUT /v1/routing

Replaces the whole set; an omitted field is cleared. Owner and Admin only (org:write).

default_model is used when a request names no model (otherwise a 400). fallback_models are tried, in order, after the models a request names, within the same depth of three a request's own models chain gets. default_sort applies when a request sets no provider.sort; with lock_sort, a request's own sort is ignored. None of these widen what a key may reach: every model still passes guardrails, the privacy policy and BYOK. Every model must be a listed, ready model id.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Request body

default_model string | null

A listed model id, used when a request names none.

fallback_models string[]
default_sort enum

The provider sort when a request sets none.

One of: price, throughput, latency, null
lock_sort boolean

Ignore a request's own provider.sort. Needs default_sort.

Responses

200 OK.
default_model string | nullrequired
fallback_models string[]required
default_sort enumrequired
One of: price, throughput, latency, null
lock_sort booleanrequired
updated_at string | nullrequired
400 `not a listed model: …`, `default_sort must be one of: price, throughput, latency`, `lock_sort needs a default_sort to lock`, or `unknown field: …`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get the IP allowlist

GET /v1/ip-allowlist

§10.3. The CIDR ranges every key in the organisation must call from, and the address the gateway sees for this request. Empty is off. Any role may read this.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
cidrs string[]required
your_address stringrequired

This request's address, as the allowlist is checked against it.

401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Set the IP allowlist

PUT /v1/ip-allowlist

Replaces the list. Owner and Admin only (org:write). Up to 100 ranges, IPv4 or IPv6; a bare address is a single host, and an address with host bits set past its prefix (10.0.0.1/8) is refused as a likely typo. [] turns the allowlist off.

While the list is not empty, every key of the organisation — inference and management, Authorization or x-api-key, and dashboard sessions — is refused from any other address with 403 `ip_not_allowed`, naming the address. A list that does not include the address saving it is a 400, so a change can never lock out the key making it. Takes effect at once on the instance serving the call and within 10 seconds everywhere else.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Request body

cidrs string[]required

CIDR ranges, e.g. 203.0.113.0/24 or 2001:db8::/32.

Responses

200 OK.
cidrs string[]required
your_address stringrequired
400 `cidrs[i] is not a CIDR range …`, `at most 100 ranges`, `unknown field: …`, or a list that does not include the caller's own address.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get the account privacy policy

GET /v1/privacy

The account level of the org → workspace → key chain (§4.2, §10.3), with the eligibility preview for it and the regions endpoints declare. Any role may read this.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
policy PrivacyPolicy
eligibility Eligibility
regions string[]
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Set the account privacy policy

PUT /v1/privacy

Replaces the whole policy; an omitted field is off. Owner and Admin only (org:write). It can only tighten routing for every key in the organisation: regions intersects with guardrails' allowed_data_regions, and an endpoint that declares no region fails it. zdr, zdr_groups and deny_training were removed: we do not record providers' retention or training terms, which are set by your own agreement with the provider. They are accepted switched off and refused switched on. Unknown fields are a 400.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Request body

zdr boolean

Removed. false is accepted; true is a 400.

zdr_groups string[]

Removed. An empty list is accepted; any group is a 400.

deny_training boolean

Removed. false is accepted; true is a 400.

regions string[]

Responses

200 OK.
policy PrivacyPolicy
eligibility Eligibility
400 `unknown field: …`, a non-boolean flag, or a list that is not slugs.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Preview a privacy policy before saving it

POST /v1/privacy/preview

How many models stay available under the policy in the body, and which do not and why. Nothing is saved. Policy only: BYOK credentials and guardrails narrow further per key.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Request body

zdr boolean

Removed. false is accepted; true is a 400.

zdr_groups string[]

Removed. An empty list is accepted; any group is a 400.

deny_training boolean

Removed. false is accepted; true is a 400.

regions string[]

Responses

200 OK.
available integerrequired
unavailable integerrequired
excluded object[]required
400 As for PUT /v1/privacy.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get input/output logging settings

GET /v1/logging

§13.1. An organisation that has never configured this gets the defaults: off, 90-day retention, consented to self_access only.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
enabled booleanrequired

§13.1: off by default. Cannot be set true in this release — gated on the review of PIA-2026-001.

key_include string[]required

Key hashes to capture. Empty means every key.

key_exclude string[]required

Exclusion wins over inclusion — a key in both is not captured.

retention_days integerrequired

At least 90 (§13.1's ~3-month floor). Content past this is deleted hourly.

locked booleanrequired

Settings frozen; further changes refused until cleared.

purposes enum[]required

What this organisation consented to. self_access is its own replay and debugging; rag would be indexing, and is a separate grant because consent is purpose-specific.

consented_at string | null

When logging was enabled. Null while it is off.

401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Configure input/output logging

PUT /v1/logging

Management key only: an inference key must not be able to change what is recorded about its own traffic.

`enabled: true` is refused in this release. It is gated on the review of PIA-2026-001, whose adequacy conclusion depends on prompt content not being stored. Every other field configures now.

locked: true freezes the settings; further changes are refused until it is cleared — and changing the setting needs the Owner or Developer role (a management key with no person behind it has full power).

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

enabled boolean

Refused when true in this release.

key_include string[]

Key hashes to capture. Empty means every key.

key_exclude string[]

Exclusion wins over inclusion — a key in both is not captured.

retention_days integer

§13.1's ~3-month floor.

locked boolean
purposes enum[]

What the organisation consents to. Must include self_access; to stop storing, disable logging instead. Consent is purpose-specific — agreeing that we store your prompts so you can replay them is not agreeing that we may index them.

Responses

200 OK.
enabled booleanrequired

§13.1: off by default. Cannot be set true in this release — gated on the review of PIA-2026-001.

key_include string[]required

Key hashes to capture. Empty means every key.

key_exclude string[]required

Exclusion wins over inclusion — a key in both is not captured.

retention_days integerrequired

At least 90 (§13.1's ~3-month floor). Content past this is deleted hourly.

locked booleanrequired

Settings frozen; further changes refused until cleared.

purposes enum[]required

What this organisation consented to. self_access is its own replay and debugging; rag would be indexing, and is a separate grant because consent is purpose-specific.

consented_at string | null

When logging was enabled. Null while it is off.

400 A refused `enabled: true`, a locked organisation, a retention below the floor, or an unknown purpose.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List guardrails

GET /v1/guardrails

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
id string
name string
model_allowlist string[]
provider_allowlist string[]
allowed_data_regions string[]

Regions an endpoint may run in, matched exactly against the endpoint deployment_region field and lower-cased on write. Empty is unrestricted. This fails CLOSED: an endpoint that declares no region is not in one, so it is refused. A region no ready endpoint declares is rejected at write time rather than refusing every request later.

budget_nanodollars string | null

Nanodollars (1e-9 USD), an integer as a string.

budget_reset string | null
include_byok_spend boolean

Count spend made on your own provider credential toward this ceiling, at IMPUTED LIST PRICE -- what the call would have cost on platform capacity, not the platform fee charged. Defaults to true: every model is BYOK-only, so a ceiling that skipped this spend would count nothing and could never bind. Set false to count only platform spend.

content_filter_builtins string[]
content_filters ContentFilter[]
filter_allowlist string[]
created_at string
assignments object[]
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Create a guardrail

POST /v1/guardrails

Assign it to a key to take effect. Policy merges organization, then workspace, then key, and each level can only narrow what the one above allows (§4.2).

Needs the Pro ($29/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

name stringrequired
limit_usd number

A budget in USD, evaluated per key.

reset_interval enum
One of: daily, weekly, monthly, lifetime
allowed_models string[]
allowed_providers string[]
allowed_data_regions string[]

Regions an endpoint may run in, matched exactly against the endpoint deployment_region field and lower-cased on write. Empty is unrestricted. This fails CLOSED: an endpoint that declares no region is not in one, so it is refused. A region no ready endpoint declares is rejected at write time rather than refusing every request later.

include_byok_in_budgets boolean

Count spend made on your own provider credential toward this ceiling, at IMPUTED LIST PRICE -- what the call would have cost on platform capacity, not the platform fee charged. Defaults to true: every model is BYOK-only, so a ceiling that skipped this spend would count nothing and could never bind. Set false to count only platform spend.

enforce_zdr object

Removed. An object of false is accepted; any true is a 400.

content_filter_builtins enum[]
content_filters ContentFilter[]
filter_allowlist string[]

Phrases a filter must never act on.

Responses

201 Created.
id string
name string
budget_nanodollars string | null

Nanodollars (1e-9 USD), an integer as a string.

budget_reset string | null
content_filter_builtins string[]
content_filters ContentFilter[]
filter_allowlist string[]
400 `name is required`, or the filter policy is invalid; the message names the filter.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 `subscription_required`: the organisation's plan does not include this. The message names the plan it needs; what was set up keeps working.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get the organisation-wide PII redaction

GET /v1/guardrails/redaction

The built-in detectors redacted from every request the organisation makes, on every plan. Any role may read this.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
builtins string[]required
updated_at string | nullrequired
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Set the organisation-wide PII redaction

PUT /v1/guardrails/redaction

Every plan, Solo included. Replaces the list; [] turns it off. Owner and Admin only (org:write). Built-in detectors only, and redact mode only: each match is replaced by its label ([email], [SECRET:openai-key]) before routing, for every key in the organisation. It merges with the key's guardrails by union, so a guardrail can add detectors, custom patterns or block mode, never remove these. Custom patterns, block mode and per-workspace or per-key policies are guardrails (Team plan): asking for them here is a 402 subscription_required on Solo and a 400 on Team.

Needs the Pro ($29/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

builtins enum[]required
action enum

Optional; redaction is the only action at this level.

One of: redact

Responses

200 OK.
builtins string[]required
updated_at string | nullrequired
400 An unknown detector or field, or (on Team) a guardrail-only field.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 Solo: a custom pattern, block mode or a per-workspace or per-key scope (`subscription_required`).
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Delete a guardrail

DELETE /v1/guardrails/{id}

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

A guardrail id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
deleted anyrequired
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Assign a guardrail

POST /v1/guardrails/{id}/assign

Needs the Pro ($29/month) plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

A guardrail id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

subject_type enumrequired
One of: key, member
subject_id stringrequired

For a key, its hash.

Responses

200 OK.
guardrail_id string
subject_type string
subject_id string
400 `subject_type and subject_id are required`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 `subscription_required`: the organisation's plan does not include this. The message names the plan it needs; what was set up keeps working.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Auth

Dashboard sign-in.

The organisations I belong to

GET /v1/auth/orgs

Every organisation the signed-in person has an active seat in, with their role and which one this session is in.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
id string
name string
role string
role_name string
current boolean
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Switch organisation

POST /v1/auth/switch

Moves the session into another organisation the person has a seat in (Set-Cookie). 404 for one they do not.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

org_id stringrequired

Responses

200 OK.
org_id string
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get the current session

GET /v1/auth/session

Authentication

Dashboard session

Responses

200 OK.
expires_at string
org_id string
key_name string | null
user object | null
auth_method string
super_admin boolean

This person is a super admin (Tech Service 4U staff: a verified email on a SUPER_ADMIN_DOMAINS domain, Owner or Admin of the platform organisation, signed in to the dashboard).

org_suspended object | null

Set while a super admin has suspended this organisation: every request is refused with 403 org_suspended.

401 No session, or it is invalid or has expired. An invalid one also clears the cookies.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Exchange a management key for a dashboard session

POST /v1/auth/session

Sets the httpOnly ar_session cookie and the script-readable ar_csrf cookie. Rate limited per client address.

Authentication

A management key, in the body as `key`.

Request body

key stringrequired

A management key.

Responses

200 OK.
expires_at string
org_id string
key_name string | null
user object | null
auth_method string
super_admin boolean

This person is a super admin (Tech Service 4U staff: a verified email on a SUPER_ADMIN_DOMAINS domain, Owner or Admin of the platform organisation, signed in to the dashboard).

org_suspended object | null

Set while a super admin has suspended this organisation: every request is refused with 403 org_suspended.

400 `key is required`.
401 `Invalid credentials` -- for any failure, a wrong key class included.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Sign out

DELETE /v1/auth/session

Always succeeds, and clears both cookies.

Authentication

Dashboard session

Responses

200 OK.
ended anyrequired
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Describe an OAuth PKCE authorization request

GET /v1/auth/authorize

What the consent screen at /auth renders. Public and rate limited: it validates the query string and echoes back the terms a key issued under it would carry. It reads nothing about any account, so a malformed request is refused before the user is asked to sign in.

Authentication

None: this endpoint is public.

Parameters

callback_url string · query

Where to return the code. Omit for the headless flow, which then requires S256 and key_label. https only, except on localhost.

code_challenge string · query

The PKCE challenge, 43-128 characters.

code_challenge_method enum · query

Must be S256, sent explicitly. plain and none are refused for every flow, redirect included: a code returned to a localhost callback can be read by any process on that machine, and only a hashed challenge keeps it from minting a key there (RFC 7636 §7.2).

key_label string · query

Names the issued key on the Keys screen. Defaults to the callback host.

Responses

200 OK.
callback_url string | nullrequired

Where the code will be returned. Null for the headless flow.

host string | nullrequired

The callback's host — what the consent screen puts in front of the user. Null when headless, and then key_label is the only name there is.

headless booleanrequired

No callback URL: the code is displayed for manual entry. Requires S256 and key_label.

key_label stringrequired

Names the issued key on the Keys screen, so access can be revoked by app.

code_challenge_method enumrequired

Always S256; plain and none are refused for every flow.

One of: S256
limit numberrequired

The spend ceiling the issued key will carry. Served rather than hardcoded in the consent screen, so the terms shown are the terms applied.

expires_in_days integerrequired

How long the issued key will last.

400 An invalid `code_challenge_method`, a challenge of the wrong length, or a headless request missing `S256` or `key_label`.
405 The callback URL is not `https` (and is not localhost), or uses a scheme other than http/https. s16.2 specifies 405 here rather than 400.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Approve an app and mint a one-time code

POST /v1/auth/authorize

Called by the consent screen once the user agrees. The request is re-validated from the body: the preceding GET is a convenience and proves nothing about this call. Codes are single-use and expire in ten minutes.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

callback_url string
code_challenge string
code_challenge_method enum
One of: S256, plain, none
key_label string

Responses

200 OK.
code stringrequired

The one-time code. Ten minutes, single use.

redirect_to string | nullrequired

The callback URL with code appended, preserving any query the app already put there. Null when headless.

expires_at stringrequired
400 As for the `GET`.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
405 As for the `GET`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Exchange a PKCE code for an API key

POST /v1/auth/keys

Unauthenticated by design -- the caller is an app that has no key yet, and the code plus its verifier are the credential. This is the one route that answers cross-origin (Access-Control-Allow-Origin: *), because a single-page app is exactly the public client PKCE exists for. Access-Control-Allow-Credentials is never set, so no cookie can ride along.

The code is consumed the moment it is looked up, including when verification then fails: a caller gets one attempt at the verifier, not a grinding window (RFC 6749 §4.1.2). The issued key is an inference key carrying a spend ceiling and an expiry, and is revocable from the Keys screen like any other.

Authentication

The one-time authorization code and its PKCE verifier, in the body.

Request body

code stringrequired

The code from the callback, or pasted from the headless flow.

code_verifier string

The PKCE verifier. Required unless the code was issued under none.

code_challenge_method enum

Optional. If sent it must MATCH the method the code was issued under; it never replaces it.

One of: S256, plain, none

Responses

200 OK.
key stringrequired

The inference key. In this response and never again — only a salted hash is stored.

hash stringrequired

Identifies the key for revocation.

label stringrequired

The app this key was issued to.

limit number | nullrequired

The spend ceiling, in US dollars.

expires_at stringrequired
400 `code is required`, or a `code_challenge_method` that disagrees with the stored one.
403 `code is invalid, already used, or expired` -- one message for all three, so a guess cannot confirm that a code exists -- or `code_verifier does not match the code_challenge`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Our SAML service provider metadata

GET /v1/auth/sso/saml/metadata

Public. Import it into your identity provider, or copy the entity ID and ACS URL from it.

Authentication

None: this endpoint is public.

Responses

200 SAML 2.0 metadata.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

SAML Assertion Consumer Service

POST /v1/auth/sso/saml/acs

Where your identity provider posts the SAMLResponse (HTTP-POST binding, form-encoded) with the RelayState. The Assertion must be signed by a certificate from your metadata, issued by your entity ID, for our entity ID, within its validity window, and answer the AuthnRequest this browser started (once; the ar_saml cookie must match the RelayState). Then as the OIDC callback: the email must be at a verified domain, and a first sign-in creates a Developer seat limited to workspaces.

Authentication

The SAML Assertion, signed by a certificate from the organisation's IdP metadata.

Responses

302 Signed in, to `/workspace`; or to the sign-in page with an error; a test sign-in returns to Settings.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Does this email sign in with SSO?

POST /v1/auth/sso/discover

Public and rate-limited. True when the address's domain is verified by an organisation with an active connection.

Authentication

None: this endpoint is public.

Request body

email stringrequired

Responses

200 OK.
sso boolean
400 Not an email address.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Start signing in with SSO

GET /v1/auth/sso/start

A browser navigation: redirects to your identity provider with PKCE, a nonce and a state cookie (ar_sso, ten minutes).

Authentication

None: this endpoint is public.

Parameters

email string · queryrequired

The work email; its domain picks the connection.

Responses

302 To the identity provider, or back to `/signin?error=sso_unknown`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

SSO callback

GET /v1/auth/sso/callback

Where your identity provider sends the browser back. The ID token's issuer, audience, expiry and nonce are checked, and the email must be at one of your verified domains. A first sign-in creates a Developer seat limited to workspaces. Errors redirect to /signin?error= sso_failed, sso_domain, seat_removed, state_mismatch, expired, cancelled or sso_unknown.

Authentication

The `state` matched to the flow cookie, and an ID token from the organisation's identity provider.

Responses

302 Signed in, to `/workspace`; or to the sign-in page with an error; a test sign-in returns to `/workspace/settings?sso_test=`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List the sign-in methods this deployment offers

GET /v1/auth/methods

Authentication

None: this endpoint is public.

Responses

200 OK.
management_key any
oauth enum[]
wallet boolean
sso boolean

True once any organisation on this deployment has an active SSO connection: the sign-in page then offers "Sign in with SSO".

429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Start signing in with Google or GitHub

GET /v1/auth/oauth/{provider}/start

A browser navigation, not an API call: redirects to the provider with PKCE and a state cookie (ar_oauth, ten minutes).

Authentication

None: this endpoint is public.

Parameters

provider enum · pathrequired

The identity provider.

Responses

302 To the provider's consent screen.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

The OAuth redirect target

GET /v1/auth/oauth/{provider}/callback

Always redirects: to /workspace with a session on success (?welcome=1 for a new user), otherwise to /signin?error= with one of cancelled, provider_error, expired, state_mismatch, no_code, not_configured, exchange_failed.

Authentication

The `state` matched to the `ar_oauth` cookie, and the provider's authorization code.

Parameters

provider enum · pathrequired

The identity provider.

code string · query

The authorization code.

state string · query

Must match the ar_oauth cookie.

error string · query

Set by the provider when sign-in did not happen.

Responses

302 To the dashboard.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Start signing in with a wallet

POST /v1/auth/wallet/nonce

Returns a Sign-In with Ethereum message to sign. The challenge lasts five minutes.

Authentication

None: this endpoint is public.

Request body

address stringrequired

Responses

200 OK.
message string
nonce string
expires_at string
400 `address must be a 0x-prefixed 20-byte hex string`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Finish signing in with a wallet

POST /v1/auth/wallet/verify

Sets the session cookies on success.

Authentication

A wallet signature over a single-use nonce from POST /v1/auth/wallet/nonce.

Request body

address stringrequired
signature stringrequired
nonce stringrequired

Responses

200 OK.
expires_at string
org_id string
is_new_user boolean
400 A field is missing, or no valid challenge exists for this address and nonce: unknown, expired, or already used. The challenge is consumed by the attempt either way.
401 `Signature does not match the address`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

SCIM

Directory sync (SCIM 2.0): your Okta or Microsoft Entra ID directory creates and deactivates people, and its groups decide which workspaces they see. Business plan or above. The /v1/scim/v2 routes take the SCIM token from Settings, not a management key. The step-by-step guide is at /docs/scim.

SCIM service provider configuration

GET /v1/scim/v2/ServiceProviderConfig

PATCH and eq filters are supported; bulk, sort and etags are not.

Authentication

SCIM token

Responses

200 A SCIM resource or error (application/scim+json).
400 A SCIM resource or error (application/scim+json).
401 A SCIM resource or error (application/scim+json).
403 A SCIM resource or error (application/scim+json).
404 A SCIM resource or error (application/scim+json).
409 A SCIM resource or error (application/scim+json).
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

SCIM resource types

GET /v1/scim/v2/ResourceTypes

User and Group.

Authentication

SCIM token

Responses

200 A SCIM resource or error (application/scim+json).
400 A SCIM resource or error (application/scim+json).
401 A SCIM resource or error (application/scim+json).
403 A SCIM resource or error (application/scim+json).
404 A SCIM resource or error (application/scim+json).
409 A SCIM resource or error (application/scim+json).
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List provisioned users

GET /v1/scim/v2/Users

Filter with userName eq "..." or externalId eq "..."; startIndex and count (at most 200) page.

Authentication

SCIM token

Responses

200 A SCIM resource or error (application/scim+json).
400 A SCIM resource or error (application/scim+json).
401 A SCIM resource or error (application/scim+json).
403 A SCIM resource or error (application/scim+json).
404 A SCIM resource or error (application/scim+json).
409 A SCIM resource or error (application/scim+json).
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Provision a user

POST /v1/scim/v2/Users

userName must be a work email at one of the organisation's verified SSO domains. A new seat is a Developer limited to workspaces; an existing account at that address is reused.

Authentication

SCIM token

Responses

201 A SCIM resource or error (application/scim+json).
400 A SCIM resource or error (application/scim+json).
401 A SCIM resource or error (application/scim+json).
403 A SCIM resource or error (application/scim+json).
404 A SCIM resource or error (application/scim+json).
409 A SCIM resource or error (application/scim+json).
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get a provisioned user

GET /v1/scim/v2/Users/{id}

One user.

Authentication

SCIM token

Parameters

id string · pathrequired

The SCIM resource id.

Responses

200 A SCIM resource or error (application/scim+json).
400 A SCIM resource or error (application/scim+json).
401 A SCIM resource or error (application/scim+json).
403 A SCIM resource or error (application/scim+json).
404 A SCIM resource or error (application/scim+json).
409 A SCIM resource or error (application/scim+json).
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Replace a user

PUT /v1/scim/v2/Users/{id}

active: false deactivates the seat.

Authentication

SCIM token

Parameters

id string · pathrequired

The SCIM resource id.

Responses

200 A SCIM resource or error (application/scim+json).
400 A SCIM resource or error (application/scim+json).
401 A SCIM resource or error (application/scim+json).
403 A SCIM resource or error (application/scim+json).
404 A SCIM resource or error (application/scim+json).
409 A SCIM resource or error (application/scim+json).
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Update a user

PATCH /v1/scim/v2/Users/{id}

PatchOp. active false (or "False") deactivates the seat: it ends, the person's dashboard sessions end, and API keys they created keep working and are flagged on GET /v1/keys (creator_deactivated). active: true does not reactivate a deactivated seat; an owner re-invites.

Authentication

SCIM token

Parameters

id string · pathrequired

The SCIM resource id.

Responses

200 A SCIM resource or error (application/scim+json).
400 A SCIM resource or error (application/scim+json).
401 A SCIM resource or error (application/scim+json).
403 A SCIM resource or error (application/scim+json).
404 A SCIM resource or error (application/scim+json).
409 A SCIM resource or error (application/scim+json).
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Deprovision a user

DELETE /v1/scim/v2/Users/{id}

The seat is deactivated as for active: false, and the person leaves every directory group.

Authentication

SCIM token

Parameters

id string · pathrequired

The SCIM resource id.

Responses

204 Done.
400 A SCIM resource or error (application/scim+json).
401 A SCIM resource or error (application/scim+json).
403 A SCIM resource or error (application/scim+json).
404 A SCIM resource or error (application/scim+json).
409 A SCIM resource or error (application/scim+json).
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List directory groups

GET /v1/scim/v2/Groups

Filter with displayName eq "..."; excludedAttributes=members omits the members.

Authentication

SCIM token

Responses

200 A SCIM resource or error (application/scim+json).
400 A SCIM resource or error (application/scim+json).
401 A SCIM resource or error (application/scim+json).
403 A SCIM resource or error (application/scim+json).
404 A SCIM resource or error (application/scim+json).
409 A SCIM resource or error (application/scim+json).
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Create a directory group

POST /v1/scim/v2/Groups

Only provisioned users can be members.

Authentication

SCIM token

Responses

201 A SCIM resource or error (application/scim+json).
400 A SCIM resource or error (application/scim+json).
401 A SCIM resource or error (application/scim+json).
403 A SCIM resource or error (application/scim+json).
404 A SCIM resource or error (application/scim+json).
409 A SCIM resource or error (application/scim+json).
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get a directory group

GET /v1/scim/v2/Groups/{id}

One group and its members.

Authentication

SCIM token

Parameters

id string · pathrequired

The SCIM resource id.

Responses

200 A SCIM resource or error (application/scim+json).
400 A SCIM resource or error (application/scim+json).
401 A SCIM resource or error (application/scim+json).
403 A SCIM resource or error (application/scim+json).
404 A SCIM resource or error (application/scim+json).
409 A SCIM resource or error (application/scim+json).
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Replace a directory group

PUT /v1/scim/v2/Groups/{id}

Its name and its whole member list.

Authentication

SCIM token

Parameters

id string · pathrequired

The SCIM resource id.

Responses

200 A SCIM resource or error (application/scim+json).
400 A SCIM resource or error (application/scim+json).
401 A SCIM resource or error (application/scim+json).
403 A SCIM resource or error (application/scim+json).
404 A SCIM resource or error (application/scim+json).
409 A SCIM resource or error (application/scim+json).
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Update a directory group

PATCH /v1/scim/v2/Groups/{id}

add / remove / replace members (also members[value eq "id"]), or replace displayName. Workspace roles follow at once.

Authentication

SCIM token

Parameters

id string · pathrequired

The SCIM resource id.

Responses

200 A SCIM resource or error (application/scim+json).
400 A SCIM resource or error (application/scim+json).
401 A SCIM resource or error (application/scim+json).
403 A SCIM resource or error (application/scim+json).
404 A SCIM resource or error (application/scim+json).
409 A SCIM resource or error (application/scim+json).
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Delete a directory group

DELETE /v1/scim/v2/Groups/{id}

Its members lose the workspace roles it gave them.

Authentication

SCIM token

Parameters

id string · pathrequired

The SCIM resource id.

Responses

204 Done.
400 A SCIM resource or error (application/scim+json).
401 A SCIM resource or error (application/scim+json).
403 A SCIM resource or error (application/scim+json).
404 A SCIM resource or error (application/scim+json).
409 A SCIM resource or error (application/scim+json).
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Directory sync settings

GET /v1/scim

The SCIM base URL, the token (prefix and dates only), how many people are provisioned, and each directory group with its workspace mappings. Owner.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
base_url string

The SCIM base URL to give your directory.

token object | null

{ prefix, created_at, last_used_at }; the token itself is shown once, as token_value when created.

users integer
groups object[]
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Create or rotate the SCIM token

POST /v1/scim/token

Returns token_value once; it replaces any previous token at once. Business plan or above; Owner.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

201 OK.
base_url string

The SCIM base URL to give your directory.

token object | null

{ prefix, created_at, last_used_at }; the token itself is shown once, as token_value when created.

users integer
groups object[]
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 Not on the Business or Enterprise plan.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Stop directory sync

DELETE /v1/scim/token

The token stops working. Provisioned people keep their seats. Not plan-gated.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
base_url string

The SCIM base URL to give your directory.

token object | null

{ prefix, created_at, last_used_at }; the token itself is shown once, as token_value when created.

users integer
groups object[]
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Map a directory group to a workspace

PUT /v1/scim/groups/{id}/workspaces/{workspaceId}

Members of the group get this role in the workspace; across several groups the highest role wins. Workspace roles only: a directory never grants an organisation role. Memberships set by hand are not changed. Business plan or above; Owner.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The directory group id.

workspaceId string · pathrequired

The workspace id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

role enumrequired
One of: admin, member

Responses

200 OK.
base_url string

The SCIM base URL to give your directory.

token object | null

{ prefix, created_at, last_used_at }; the token itself is shown once, as token_value when created.

users integer
groups object[]
400 role must be admin or member.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 Not on the Business or Enterprise plan.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Remove a group mapping

DELETE /v1/scim/groups/{id}/workspaces/{workspaceId}

Members lose the role it gave them in that workspace.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The directory group id.

workspaceId string · pathrequired

The workspace id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
base_url string

The SCIM base URL to give your directory.

token object | null

{ prefix, created_at, last_used_at }; the token itself is shown once, as token_value when created.

users integer
groups object[]
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Federation

Workload identity federation: a workload exchanges the identity token its platform already gives it for a short-lived inference token, so no long-lived key is stored next to it. Policies are Owner or Admin, on every plan. The step-by-step guide is at /docs/federation.

Exchange a workload identity token for an inference token

POST /v1/oauth/token

RFC 8693 token exchange (§16.3). Public: the subject token is the credential. Send the identity token your platform gives the workload -- a GitHub Actions OIDC token, a cloud workload identity, a Kubernetes service-account token -- with the id of the federation policy it should match. The token must be signed RS256 or ES256 by the policy's issuer (checked against the issuer's published keys), unexpired, with iss, sub and aud exactly the policy's and every claim check holding. The answer is a Bearer token scoped inference, valid for at most 900 seconds and never past the subject token's own exp, accepted wherever an inference key is and acting as the key the policy names: its limit, workspace, IP allowlist and pause apply, and revoking the key ends the token. Form-encoded or JSON; body at most 32 KB, subject token at most 16 KB. Errors are RFC 6749 bodies (error, error_description).

Authentication

The workload's signed identity token, in the body as `subject_token`, checked against the federation policy.

Responses

200 The access token. `Cache-Control: no-store`.
access_token stringrequired
issued_token_type enumrequired
One of: urn:ietf:params:oauth:token-type:access_token
token_type enumrequired
One of: Bearer
expires_in integerrequired
scope enumrequired
One of: inference
400 `unsupported_grant_type`, `invalid_request`, or `invalid_grant` with what failed: no active policy with that id, the key it acts as is not active, or which check the subject token failed.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.
503 `temporarily_unavailable`: federation is not enabled on this deployment.

The keys our access tokens are signed with

GET /v1/oauth/jwks

Public. A JWK Set of ES256 public keys: the current signing key and, during a rotation, the previous one. Empty when federation is not enabled. Cacheable for five minutes.

Authentication

None: this endpoint is public.

Responses

200 A JWK Set.
keys object[]required
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

List federation policies

GET /v1/federation/policies

Owner or Admin. enabled says whether this deployment can issue tokens at all.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 The policies.
id stringrequired
name stringrequired
issuer_url stringrequired
subject stringrequired
audience stringrequired
acts_as_key_hash stringrequired

The inference key an exchanged token acts as.

claim_checks objectrequired
status enumrequired
One of: active, paused
last_used_at string | null
created_at string
updated_at string
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Create a federation policy

POST /v1/federation/policies

Which workload tokens are accepted (issuer_url, subject, audience, claim_checks) and the inference key the exchanged token acts as (acts_as_key_hash, an active inference key of this organisation). The issuer must be a public https URL serving an OIDC discovery document with a jwks_uri. claim_checks: up to 20 {"claim": expected}; a string, number or boolean claim must equal it, an array claim must contain it. Owner or Admin.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

name stringrequired
issuer_url stringrequired
subject stringrequired
audience stringrequired
acts_as_key_hash stringrequired
claim_checks object

Responses

201 OK.
id stringrequired
name stringrequired
issuer_url stringrequired
subject stringrequired
audience stringrequired
acts_as_key_hash stringrequired

The inference key an exchanged token acts as.

claim_checks objectrequired
status enumrequired
One of: active, paused
last_used_at string | null
created_at string
updated_at string
400 A missing or malformed field, an issuer that is not a public https URL, or a key that is not an active inference key of this organisation.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Change or pause a federation policy

PATCH /v1/federation/policies/{id}

Any of name, issuer_url, subject, audience, claim_checks, and status (active or paused). Pausing stops new exchanges at once; tokens already issued run out their clock (at most 900 seconds). Owner or Admin.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The policy id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

name string
issuer_url string
subject string
audience string
claim_checks object
status enum
One of: active, paused

Responses

200 OK.
id stringrequired
name stringrequired
issuer_url stringrequired
subject stringrequired
audience stringrequired
acts_as_key_hash stringrequired

The inference key an exchanged token acts as.

claim_checks objectrequired
status enumrequired
One of: active, paused
last_used_at string | null
created_at string
updated_at string
400 A malformed field, or nothing to change.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Delete a federation policy

DELETE /v1/federation/policies/{id}

Stops new exchanges at once; tokens already issued run out their clock (at most 900 seconds). To end those too, revoke the key the policy acts as. Owner or Admin.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The policy id.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

204 Deleted.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

SSO

Single sign-on through your own identity provider, over OpenID Connect or SAML 2.0 (Okta, Microsoft Entra ID, Google Workspace, ADFS, any OIDC issuer or SAML IdP). Business plan or above; Owner or Admin. The step-by-step guide is at /docs/sso.

The single sign-on setup

GET /v1/sso

The connection to your identity provider, your email domains with the TXT record each needs, and the callback URL to register. Owner or Admin.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

200 OK.
callback_url string

Register this exact URL as the redirect (sign-in) URI of your OIDC app.

saml_sp object
connection object | null

{ protocol: oidc|saml, status: pending|active|disabled, require_sso, tested_at, created_at, issuer, client_id, saml_entity_id, saml_sso_url }; the client secret is never returned.

domains object[]
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Connect your identity provider

PUT /v1/sso

Your OIDC app's issuer, client id and client secret. The issuer's discovery document (<issuer>/.well-known/openid-configuration) is read now and must name the same issuer. A new connection, or a changed issuer or client id, is pending (with require_sso off) until a test sign-in succeeds and it is activated again; a new secret alone keeps the connection as it is. The secret is sealed and never returned; omit it to keep the stored one. Business plan or above; Owner or Admin.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

issuer stringrequired
client_id stringrequired
client_secret string

Responses

200 OK.
callback_url string

Register this exact URL as the redirect (sign-in) URI of your OIDC app.

saml_sp object
connection object | null

{ protocol: oidc|saml, status: pending|active|disabled, require_sso, tested_at, created_at, issuer, client_id, saml_entity_id, saml_sso_url }; the client secret is never returned.

domains object[]
400 A missing field, or an issuer whose discovery document cannot be read or names another issuer.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 Not on the Business or Enterprise plan (`subscription_required`).
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Require single sign-on

PATCH /v1/sso

With require_sso: true, people at your verified domains can only sign in through your identity provider; Google, GitHub and wallet sign-in and switching in from another organisation are refused for them (403 sso_required). Your Owners are exempt and keep their original method, so an identity-provider outage cannot lock you out. Turning it on ends the current dashboard sessions of the people it applies to. Needs an active connection.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

require_sso booleanrequired

Responses

200 OK.
callback_url string

Register this exact URL as the redirect (sign-in) URI of your OIDC app.

saml_sp object
connection object | null

{ protocol: oidc|saml, status: pending|active|disabled, require_sso, tested_at, created_at, issuer, client_id, saml_entity_id, saml_sso_url }; the client secret is never returned.

domains object[]
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 Not on the Business or Enterprise plan.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
409 The connection is not active.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Remove the connection

DELETE /v1/sso

Your verified domains are kept. Not plan-gated.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
deleted boolean
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Connect a SAML identity provider

PUT /v1/sso/saml

Either metadata_xml (the IdP metadata file exported from Okta, Entra, Google Workspace, ADFS...) or entity_id, sso_url (HTTP-Redirect binding, https) and certificate (the signing certificate, PEM or base64). Replaces an OIDC connection. A new identity provider is pending (with require_sso off) until a test sign-in passes and it is activated again; a renewed certificate keeps the connection as it is. Assertions must be signed. Business plan or above; Owner or Admin.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

metadata_xml string
entity_id string
sso_url string
certificate string

Responses

200 OK.
callback_url string

Register this exact URL as the redirect (sign-in) URI of your OIDC app.

saml_sp object
connection object | null

{ protocol: oidc|saml, status: pending|active|disabled, require_sso, tested_at, created_at, issuer, client_id, saml_entity_id, saml_sso_url }; the client secret is never returned.

domains object[]
400 Not identity provider metadata, no HTTP-Redirect sign-on URL, an http URL, or no signing certificate.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 Not on the Business or Enterprise plan.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Activate single sign-on

POST /v1/sso/activate

Needs a successful test sign-in (GET /v1/auth/sso/test) since the connection was last changed, and at least one verified domain. Business plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
callback_url string

Register this exact URL as the redirect (sign-in) URI of your OIDC app.

saml_sp object
connection object | null

{ protocol: oidc|saml, status: pending|active|disabled, require_sso, tested_at, created_at, issuer, client_id, saml_entity_id, saml_sso_url }; the client secret is never returned.

domains object[]
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 Not on the Business or Enterprise plan.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
409 No successful test sign-in yet, or no verified domain.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Turn single sign-on off

POST /v1/sso/disable

Sign-in through your identity provider stops at once, and require_sso is turned off. Not plan-gated.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
callback_url string

Register this exact URL as the redirect (sign-in) URI of your OIDC app.

saml_sp object
connection object | null

{ protocol: oidc|saml, status: pending|active|disabled, require_sso, tested_at, created_at, issuer, client_id, saml_entity_id, saml_sso_url }; the client secret is never returned.

domains object[]
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Add an email domain

POST /v1/sso/domains

Returns the TXT record to add at the domain's root (autonomousrelay-verification=<token>). At most 20 domains. Business plan or above.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Request body

domain stringrequired

Such as acme.com.

Responses

201 OK.
callback_url string

Register this exact URL as the redirect (sign-in) URI of your OIDC app.

saml_sp object
connection object | null

{ protocol: oidc|saml, status: pending|active|disabled, require_sso, tested_at, created_at, issuer, client_id, saml_entity_id, saml_sso_url }; the client secret is never returned.

domains object[]
400 Not a domain name, or the 20-domain cap.
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 Not on the Business or Enterprise plan.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Verify an email domain

POST /v1/sso/domains/{domain}/verify

Looks the TXT record up now. DNS changes can take up to an hour to be visible. A domain is verified by at most one organisation.

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

domain string · pathrequired

The domain.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
callback_url string

Register this exact URL as the redirect (sign-in) URI of your OIDC app.

saml_sp object
connection object | null

{ protocol: oidc|saml, status: pending|active|disabled, require_sso, tested_at, created_at, issuer, client_id, saml_entity_id, saml_sso_url }; the client secret is never returned.

domains object[]
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
402 Not on the Business or Enterprise plan.
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
409 The record is not visible yet, or another organisation already verified this domain.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Remove an email domain

DELETE /v1/sso/domains/{domain}

Authentication

Management key (ar-mgmt-…) · Dashboard session

Parameters

domain string · pathrequired

The domain.

x-csrf-token string · header

Required with a session cookie on any method but GET: the value of the ar_csrf cookie. Not used with a bearer key.

Responses

200 OK.
callback_url string

Register this exact URL as the redirect (sign-in) URI of your OIDC app.

saml_sp object
connection object | null

{ protocol: oidc|saml, status: pending|active|disabled, require_sso, tested_at, created_at, issuer, client_id, saml_entity_id, saml_sso_url }; the client secret is never returned.

domains object[]
401 No key; an invalid, disabled or expired key; or an invalid session. The body reports `code: 401` and `invalid_credentials`. (Until 2026-09-27 every management route sent the 403 envelope here; `requireManagementKey` in lib/auth.ts now decides the envelope from the status.)
403 An inference key on a management route, or a session request without a valid `x-csrf-token`.
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Run a test sign-in

GET /v1/auth/sso/test

A browser navigation from a signed-in Owner or Admin: a sign-in through your own connection, even while it is pending. It records the success (which POST /v1/sso/activate needs) and signs nobody in; the result comes back to /workspace/settings?sso_test=ok|....

Authentication

Management key (ar-mgmt-…) · Dashboard session

Responses

302 To the identity provider, or back to Settings with the reason it could not start.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Runs

Agent runs (§17.1): one goal, many turns, one budget enforced across all of them.

List runs

GET /v1/runs

Newest first, for the calling key's organization. An inference key, or a management key or dashboard session whose role grants usage:read.

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Parameters

limit integer · query

Page size, capped at 100.

status enum · query

One status only.

Responses

200 OK.
id stringrequired
status enumrequired

exhausted is set by settlement when spend reaches the budget, and paused by the §17.4 runaway detector. A caller may set only completed or cancelled — a client able to claim either of the other two could report a breach that never happened.

paused is the one non-open status that is recoverable: see POST /v1/runs/{id}/resume.

One of: open, paused, completed, exhausted, cancelled
goal string | null
budget numberrequired

The ceiling. Required at creation, always positive: a run without one is what §17.1 exists to prevent.

spent numberrequired

Settled. Imputed list price — what the turns were worth at catalog prices, not money billed through us. Under the BYOK-only policy inference is billed by your own provider, and a run sized on our fee would report every turn as free (§9.6).

reserved numberrequired

Worst-case imputed cost committed by turns admitted but not yet settled. Published because budget - spent alone overstates the headroom a run actually has.

remaining numberrequired

budget - spent - reserved.

max_turns integer | null
turns_used integerrequired
max_tool_calls integer | null
tool_calls_used integerrequired
model_policy object
max_cost_per_minute number | null

§17.4 threshold, in USD. Null means the cost-velocity detector is off for this run.

max_tool_calls_without_progress integer | null

§17.4 threshold. Null means the no-progress detector is off for this run.

tool_calls_without_progress integer

Consecutive tool calls with no answering turn between them. Reset to 0 by any turn finishing with something other than tool_calls.

pause_reason string | null

Which detector paused this run. Set only by settlement — the actionable half, since a paused run without a stated cause leaves you guessing which threshold to raise.

One of: cost_velocity, no_progress, null
paused_at string | null
created_at string
updated_at string
closed_at string | null
400 `status must be one of: …`.
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Open a run

POST /v1/runs

A run is one goal across many turns, with one budget enforced across all of them. Pass its id as run_id on each completion.

Inference class, not management: the thing that opens a run is the agent, and requiring a management key would hand every agent the credential that mints keys and moves budgets. The run is scoped to the organization, so a supervisor and its workers may hold different keys and share one.

Authentication

Inference key (ar-v1-…)

Request body

goal string

Free text, at most 2000 characters.

budget_usd numberrequired

Required, and positive. There is no way to open an uncapped run.

max_turns integer

Bounds how many times the agent may ask. A failed turn refunds its money but not its turn — see packages/docs/RUNS.md.

max_tool_calls integer

Checked at turn admission, so a single turn can exceed it by its own call count.

model_policy object
max_cost_per_minute_usd number

§17.4 runaway detector, opt-in. Pauses the run when spend inside a tumbling one-minute window exceeds this. Omit to leave it off.

The window is tumbling, not sliding: a burst straddling a boundary can be split across two windows and escape. Acceptable for a safety net whose job is to stop a loop running overnight; it is not a billing control.

max_tool_calls_without_progress integer

§17.4 runaway detector, opt-in. Pauses the run after this many consecutive tool calls with no answering turn between them — "progress" meaning a turn that finished with something other than tool_calls. Omit to leave it off.

Responses

201 Opened.
id stringrequired
status enumrequired

exhausted is set by settlement when spend reaches the budget, and paused by the §17.4 runaway detector. A caller may set only completed or cancelled — a client able to claim either of the other two could report a breach that never happened.

paused is the one non-open status that is recoverable: see POST /v1/runs/{id}/resume.

One of: open, paused, completed, exhausted, cancelled
goal string | null
budget numberrequired

The ceiling. Required at creation, always positive: a run without one is what §17.1 exists to prevent.

spent numberrequired

Settled. Imputed list price — what the turns were worth at catalog prices, not money billed through us. Under the BYOK-only policy inference is billed by your own provider, and a run sized on our fee would report every turn as free (§9.6).

reserved numberrequired

Worst-case imputed cost committed by turns admitted but not yet settled. Published because budget - spent alone overstates the headroom a run actually has.

remaining numberrequired

budget - spent - reserved.

max_turns integer | null
turns_used integerrequired
max_tool_calls integer | null
tool_calls_used integerrequired
model_policy object
max_cost_per_minute number | null

§17.4 threshold, in USD. Null means the cost-velocity detector is off for this run.

max_tool_calls_without_progress integer | null

§17.4 threshold. Null means the no-progress detector is off for this run.

tool_calls_without_progress integer

Consecutive tool calls with no answering turn between them. Reset to 0 by any turn finishing with something other than tool_calls.

pause_reason string | null

Which detector paused this run. Set only by settlement — the actionable half, since a paused run without a stated cause leaves you guessing which threshold to raise.

One of: cost_velocity, no_progress, null
paused_at string | null
created_at string
updated_at string
closed_at string | null
400 `budget_usd is required and must be a positive number`, or a malformed cap or `model_policy`.
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Get a run and its tree

GET /v1/runs/{id}

§17.1's tree: turns, per-turn cost, cache hit rate, failed tool calls, model switches and a terminal status. An inference key, or a management key or dashboard session whose role grants usage:read.

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The run id.

Responses

200 OK.
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
404 Not found, or not in your organization.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Close a run

POST /v1/runs/{id}/close

Terminal. An open or paused run closes; closing an already-closed run is a 409 naming its current status, never a silent no-op. An inference key, or a management key or dashboard session whose role grants config:write.

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The run id.

Request body

status enum
One of: completed, cancelled
Default: "completed"

Responses

200 OK.
id stringrequired
status enumrequired

exhausted is set by settlement when spend reaches the budget, and paused by the §17.4 runaway detector. A caller may set only completed or cancelled — a client able to claim either of the other two could report a breach that never happened.

paused is the one non-open status that is recoverable: see POST /v1/runs/{id}/resume.

One of: open, paused, completed, exhausted, cancelled
goal string | null
budget numberrequired

The ceiling. Required at creation, always positive: a run without one is what §17.1 exists to prevent.

spent numberrequired

Settled. Imputed list price — what the turns were worth at catalog prices, not money billed through us. Under the BYOK-only policy inference is billed by your own provider, and a run sized on our fee would report every turn as free (§9.6).

reserved numberrequired

Worst-case imputed cost committed by turns admitted but not yet settled. Published because budget - spent alone overstates the headroom a run actually has.

remaining numberrequired

budget - spent - reserved.

max_turns integer | null
turns_used integerrequired
max_tool_calls integer | null
tool_calls_used integerrequired
model_policy object
max_cost_per_minute number | null

§17.4 threshold, in USD. Null means the cost-velocity detector is off for this run.

max_tool_calls_without_progress integer | null

§17.4 threshold. Null means the no-progress detector is off for this run.

tool_calls_without_progress integer

Consecutive tool calls with no answering turn between them. Reset to 0 by any turn finishing with something other than tool_calls.

pause_reason string | null

Which detector paused this run. Set only by settlement — the actionable half, since a paused run without a stated cause leaves you guessing which threshold to raise.

One of: cost_velocity, no_progress, null
paused_at string | null
created_at string
updated_at string
closed_at string | null
400 `status must be one of: completed, cancelled`.
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
404 Not found, or not in your organization.
409 The run is already `completed`, `cancelled` or `exhausted`. The body reports `code: 400` and `invalid_request`, although the status is 409.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Resume a run the runaway detector paused

POST /v1/runs/{id}/resume

The counterpart to pausing rather than cancelling (§17.4): the detector stops a run so a human can look at it, and this is how they say "I looked, carry on".

Clears the counters that caused the pause — without that it would pause again on the very next settlement and this call would appear to do nothing. The budget and turn counts are deliberately not reset: those are the caps the run was opened under, and a pause is not an amnesty.

Only a paused run resumes. exhausted and cancelled are genuinely finished, and resuming those would be a way to reopen a budget that has already bound.

An inference key, or a management key or dashboard session whose role grants config:write.

Authentication

Inference key (ar-v1-…) · Management key (ar-mgmt-…) · Dashboard session

Parameters

id string · pathrequired

The run id.

Responses

200 OK.
id stringrequired
status enumrequired

exhausted is set by settlement when spend reaches the budget, and paused by the §17.4 runaway detector. A caller may set only completed or cancelled — a client able to claim either of the other two could report a breach that never happened.

paused is the one non-open status that is recoverable: see POST /v1/runs/{id}/resume.

One of: open, paused, completed, exhausted, cancelled
goal string | null
budget numberrequired

The ceiling. Required at creation, always positive: a run without one is what §17.1 exists to prevent.

spent numberrequired

Settled. Imputed list price — what the turns were worth at catalog prices, not money billed through us. Under the BYOK-only policy inference is billed by your own provider, and a run sized on our fee would report every turn as free (§9.6).

reserved numberrequired

Worst-case imputed cost committed by turns admitted but not yet settled. Published because budget - spent alone overstates the headroom a run actually has.

remaining numberrequired

budget - spent - reserved.

max_turns integer | null
turns_used integerrequired
max_tool_calls integer | null
tool_calls_used integerrequired
model_policy object
max_cost_per_minute number | null

§17.4 threshold, in USD. Null means the cost-velocity detector is off for this run.

max_tool_calls_without_progress integer | null

§17.4 threshold. Null means the no-progress detector is off for this run.

tool_calls_without_progress integer

Consecutive tool calls with no answering turn between them. Reset to 0 by any turn finishing with something other than tool_calls.

pause_reason string | null

Which detector paused this run. Set only by settlement — the actionable half, since a paused run without a stated cause leaves you guessing which threshold to raise.

One of: cost_velocity, no_progress, null
paused_at string | null
created_at string
updated_at string
closed_at string | null
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
404 Not found, or not in your organization.
409 The run is not paused. The body reports `code: 400` and `invalid_request`, although the status is 409.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

MCP

Model Context Protocol, for coding agents. Every tool adapts a route documented above.

Not offered: this server has no server-initiated stream

GET /v1/mcp

Always 405. MCP's transport requires that status from a server offering no SSE stream, and it is answered deliberately rather than left to a 404, because a client probing for a stream must read "not offered" rather than "wrong URL".

Authentication

None: this endpoint is public.

Responses

405 `This MCP server offers no server-initiated stream; use POST.` The body reports `code: 400` and `invalid_request`, although the status is 405.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Call the MCP server

POST /v1/mcp

JSON-RPC 2.0 over MCP's Streamable HTTP transport, protocol revision 2025-06-18. Methods: initialize, tools/list, tools/call, ping, and the notifications/* messages.

Every tool is an adapter over a route documented elsewhere in this file, dispatched through this gateway's own router — so an MCP call passes the same authentication, rate limits, spend ceilings, guardrails and ledger as the HTTP call it stands for. See packages/docs/MCP.md.

Requires an inference key; a management key gets 403, as it does on /v1/chat/completions. The OAuth PKCE flow (/auth) is the intended way for a coding tool to obtain one.

Stateless: no Mcp-Session-Id, because several instances run behind a load balancer with no sticky routing. JSON-RPC batching is refused — revision 2025-06-18 removed it.

Authentication

Inference key (ar-v1-…)

Request body

jsonrpc anyrequired
id string | integer | null

Omit the field entirely for a notification, which is answered with 202 and no body. An explicit null is a request, not a notification.

method stringrequired

e.g. tools/call.

params object

Responses

200 A JSON-RPC response. A TOOL that failed returns a success envelope with `result.isError: true`; `error` is reserved for protocol faults — a malformed request, an unknown method, an unknown tool.
jsonrpc anyrequired
id string | integer | nullrequired
result object

For tools/call: content (an array of text blocks) and isError.

error object
202 A notification was accepted. No body, by design: a client must not try to correlate a response to a request it never made.
401 No key; an invalid, disabled or expired key; or an invalid session.
403 A management key, or a dashboard session where only an inference key will do; or a session request without a valid `x-csrf-token`. The body reports `code: 403` and `permission_denied`. (Until 2026-09-27 it carried the 401 envelope; `ERRORS.fromAuth` now decides the envelope from the status.)
429 `MCP rate limit exceeded`. Every POST counts against a per-key MCP quota of 300 a minute and 50,000 a day, separate from the inference quota that a `tools/call` also meets inside the route it calls. The `X-RateLimit-*` headers are on every response.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

The MCP Server Card

GET /v1/mcp/server-card

This MCP server's Server Card (SEP-2127), for discovery before connecting: its identity, the Streamable HTTP endpoint, the protocol revisions it speaks, and the Authorization header it needs. It lists no tools; tools/list does, at runtime. Served as application/mcp-server-card+json, readable from any origin, cacheable for an hour, and listed in the site's agent catalogue (/.well-known/ard.json).

Authentication

None: this endpoint is public.

Responses

200 The card. Its schema is `https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json`.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Meta

Health, region, and this document.

Health

GET /health

Public; for load balancers and uptime checks. A 500 means the database is unreachable.

Liveness, the version this document already publishes, and whether the rate limiter is shared across instances — nothing here varies with account activity.

Authentication

None: this endpoint is public.

Responses

200 Serving.
status anyrequired
version string

The same value this document publishes as info.version.

rate_limiting object
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

Receive a Content-Security-Policy violation report

POST /v1/csp-report

For browsers, not for you: the dashboard's CSP names this path in report-uri. Accepts the legacy application/csp-report document and the Reporting API's application/reports+json array; the violation's document, directive, blocked URI and source are logged (each clipped to 200 characters, at most ten per report) and nothing is stored. Unauthenticated and rate-limited per address like the catalog.

Authentication

None: this endpoint is public.

Responses

204 Logged.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
451 Refused by jurisdiction: the edge reported a subdivision this service does not operate in yet. **`code` is a string here, and there is no `metadata`.**
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

The caller's coarse region

GET /v1/geo

As the edge reported it. The dashboard uses it to choose a language, so it is never refused by region.

Authentication

None: this endpoint is public.

Responses

200 OK.
region string | null
subdivision string | null
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

This document, as JSON

GET /openapi.json

Authentication

None: this endpoint is public.

Responses

200 The OpenAPI 3.1 document.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

This document, as YAML

GET /openapi.yaml

Authentication

None: this endpoint is public.

Responses

200 The OpenAPI 3.1 document.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

This document, as JSON

GET /v1/openapi.json

Authentication

None: this endpoint is public.

Responses

200 The OpenAPI 3.1 document.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
500 An unexpected failure. `metadata.request_id` identifies it in our logs.

This document, as YAML

GET /v1/openapi.yaml

Authentication

None: this endpoint is public.

Responses

200 The OpenAPI 3.1 document.
429 Rate limited. `X-RateLimit-Reset` says when the window resets.
500 An unexpected failure. `metadata.request_id` identifies it in our logs.