Agent reference
The depth behind Add an agent.
The Agents row reports the source hook separately from routing and MCP liveness. command and
http mean installed; missing means upgrade Connect. An absent report is unknown, including
a gateway restart or another replica. Reports expire after one minute and never license trimming.
Status vocabulary
The Agents card reads each key from GET /admin/v1/keys?type=service and the gateway's schema version from GET /admin/health (schema.version). Each status is its own state; none stands in for another.
| Status | Meaning |
|---|---|
| Checking… | The keys or health read is still in flight. |
| Not connected | No request has authenticated with this key. |
| Active | At least one runtime the key used in the last day can call anyray_retrieve, and each of the others can too, is forwarded unchanged, or sent no tool calls. With no per-runtime verdict yet, retrieval evidence within six hours of the last request. |
| Codex ✓ · Claude Code: MCP missing | One runtime is wired and another is not. The pill names both. Clicking the row opens Setup at step 3. |
| MCP disconnected | No runtime on the key can call anyray_retrieve, or a runtime is forwarded unchanged with no read path on some or all of its calls (readPath: absent or partial). Clicking the row opens Setup at step 3. |
| Active · no tool calls | Every request the key sent in the last day forbade tool calls, so there is nothing to retrieve and nothing to wire. |
| Active · forwarded unchanged | Every runtime on the key that calls tools reads parity with its read path ready or unreported: no request got a retrieval-dependent trim or a handle, so there is nothing to read back and nothing to wire. The detail line says which (see readPath). |
| Expired | expiresAt has passed. |
| Revoked | Revoked. Hidden from the roster. |
A gateway older than migration 0083_client_keys_seen stamps none of these fields, so its keys read Not connected. Upgrade the gateway; the host is fine.
Retrieval per runtime
One agent key usually signs for several runtimes, and a key-level check latches on whichever proves retrieval first. GET /admin/v1/keys/:id returns retrievalRuntimes for every key. A service key gets one entry per client_tool seen in the last 24 hours; a personal key reads state: "unmeasured_personal_key", and an older gateway omits the field:
| Verdict | Meaning |
|---|---|
wired | The runtime declared the retrieval tool or the gateway's /mcp, the gateway served retrieval for it, or a deferred catalog latched. |
unlatched | The runtime finds tools at run time (Codex 0.153 and later, a deferred Claude catalog), so its requests cannot name the tool and nothing vouches for it yet. |
unwired | Requests that can call tools, with no way to retrieve, on a deployment that opted their class into served retrieval. The MCP server is missing from that runtime. |
parity | Requests that can call tools went out with no retrieval-dependent trim and no handle minted. Either the deployment has not opted no_discovery into served retrieval, or the request is an app tool loop that declared anyray_retrieve while harnessRetrieval is off; readPath says which. Nothing needs reading back, so nothing needs wiring. One request without that proof makes the runtime unwired. |
toolless_only | Every request forbade tool calls. Nothing to wire. |
unknown | Requests landed with no evidence either way. |
The counts are metadata: requests per evidence class, no content. state: "unavailable" means the gateway could not read its spend store, which is not the same as a measured day with no runtimes.
Read path while forwarded unchanged
A parity runtime also carries readPath, which says why nothing was trimmed. GET /connect/self-check reports it on the same runtime, and the Agents card shows it on the detail line.
readPath | Card reads | Meaning |
|---|---|---|
declared | Read path ready | The app tool loop declared anyray_retrieve, and harnessRetrieval is off. Turning it on lets the gateway trim this loop. |
absent | No read path, finish step 2 | The loop declares no anyray_retrieve, and its class is not opted into served retrieval. |
partial | Read path on some steps, finish step 2 | Some requests declared it and some did not. |
| The field is missing | Forwarded unchanged | The gateway predates the field, or could not tell. Not the same as absent. |
Nothing was trimmed on any of these, so nothing was lost, and problems stays 0. harnessRetrieval is on
by default, so the card treats absent and partial as unfinished wiring: the pill reads MCP
disconnected and step 3 points back to step 2. declared and a missing field stay Active · forwarded
unchanged.
Anyray skill per runtime
GET /admin/v1/keys/:id returns skillRuntimes, and GET /connect/self-check returns the same report as
skill for the calling key. It says whether each runtime listed the anyray skill to its model in the last
24 hours. A personal key reads state: "unmeasured_personal_key", unavailable means the gateway could not
read its spend store, and an older gateway omits the field.
Each runtime may also include invoked (older gateways omit it): whether the model loaded the skill
body, and whether it was the current copy.
state per runtime | Meaning |
|---|---|
loaded | A request listed the skill to the model. |
not_loaded | Requests could list skills, and none listed anyray. Install it where the app's skill loader reads it. |
no_loader | The loop has no skill loader, such as a plain Messages loop or LangChain. Nothing to install. |
unknown | Only requests the gateway cannot judge. |
invoked | Meaning |
|---|---|
current | The newest recorded load used the body this gateway serves now. |
outdated | The newest recorded load used a body other than the body this gateway serves now. |
never | The runtime could load the skill, but no request invoked it. |
unknown | No load evidence is available, or the gateway cannot judge it. |
Step 3 of Add agent shows the result once the key connects. The skill does not count in problems.
Read-back
Read-back shows whether a wired runtime actually retrieves: handles minted in the last 24 hours, and anyray_retrieve reads that resolved or missed. The agent's Info tab shows one Read-back line per runtime, from retrievalRuntimes.readBack on GET /admin/v1/keys/:id.
| Line | Meaning |
|---|---|
| Needs gateway upgrade | This gateway predates read-back. The host is fine. |
| Not available | The gateway could not read its spend store. |
| No handles yet | Nothing was minted or read in the window. |
<tool>: N handles, none read yet | Normal. The model had no need for the trimmed content. |
<tool>: N handles, M read back | Reads resolve. |
<tool>: N handles, M read missed | A few reads missed, too few to call a failure. |
<tool>: M read back, handles not countable | Older rows in the window hide the minted count. |
<tool>: reads failing | 3 or more misses, and at least half of all reads missed. |
<tool>: served retrieval failing | Gateway-served retrieval hit 3 or more of one failure: the stash could not answer, the loop errored, or a request reached the round cap. |
<tool>: N handles minted, cannot be read | 3 or more handles went to a runtime whose verdict is unwired or unlatched. |
The last three are amber and the only states that alert. The control plane checks every roster hourly and emails the billing contact (or the owners) once per agent until reads recover. An agent already flagged as routing without retrieval gets that email instead. Handles minted and never read never alert.
Optional client tools
GET /admin/v1/keys/:id also returns clientTools, what the host's MCP server reports about the tools Connect can offer but does not have to. The Agents card shows it as Client tools. Four answers, and they send you to four different places:
clientTools | Meaning |
|---|---|
| The field is missing | This gateway predates the report. Upgrade the gateway; the host is fine. |
state: "never_reported" | No MCP server on this key has reported. Usually a Connect older than the report. |
state: "reported", readBatch: "enabled" | Batch reads are on and servable. |
state: "reported", readBatch: "off" | Someone set the switch to false in the host's Connect profile. |
state: "reported", readBatch: "invalid_policy" | The host's clientToolPolicies subtree is malformed. |
state: "reported", readBatch: "unsupported_platform" | The operating system has no confined reader. |
state: "reported", readBatch: "unsupported_architecture" | The CPU architecture has no confined reader. |
state: "reported", readBatch: "native_binding_unavailable" | The native reader failed to load in the MCP server. |
A reported answer carries observedAt and, when the heartbeat named one, connectVersion: the build of the MCP server that reported, which can trail the installed build until its client restarts.
Calls to Anyray's own MCP tools are counted per hour and returned deployment-wide on GET /admin/v1/fleet/seats as mcpToolCalls, with byTool totals for the last 24 hours. These are attempts, including refused and failed calls. state: "unavailable" means the store could not be read. Batch reads run locally; their heartbeat reports carry separate completion counts. Counts and Anyray's own tool names only, never an argument or a path.
Last active is lastSeenAt (current to within 5 minutes) with lastSeenTool, the gateway's id for the client that made the request: codex, claude-code, openclaw, shell-env, or the tool from x-anyray-metadata.
When a Claude runtime has no read path, the card opens the tab that fixes it: Host for a terminal or headless claude -p process (Connect registers the read path), Claude for an Agent SDK app (your code registers it). The gateway tells them apart from the entrypoint of Claude Code's User-Agent and keeps only a fixed label, lastSeenEntrypoint. A key without it opens the Claude tab.
Key records include hookRuns, one tool, running Connect build and last run time per hook. A missing field means the gateway needs an upgrade.
The post-mint dialog polls the same record every 5 seconds for up to 10 minutes and stops once every runtime it has seen is wired.
Routing
Routing on the Add agent dialog, or on the agent's detail view later, binds the key to one provider and one of its named keys so its spend lands on a separate upstream account. Left unset, the agent follows the org routing config. Changing it changes nothing on the host.
Retrieval tools
The gateway MCP server omits its extra instruction block by default. Its retrieval tools remain callable, and a model asks for a marked payload when it needs one. The block is a nudge for weaker models, and turning it on adds prefix tokens to every request, so the default stays off. No host configuration is involved either way.
Mint from the admin API
For a provisioning script. POST /admin/v1/keys with type: "service" needs an admin token with the clientkeys:manage capability (the keys:write scope).
curl https://<your-anyray-gateway>/admin/v1/keys \
-H "Authorization: Bearer $ANYRAY_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "service",
"name": "synthetic-deploy-agent",
"team": "platform",
"monthlyBudgetUsd": 50,
"routingProvider": "bedrock",
"routingProviderKeyId": "ci-account"
}'
service. The same route mints user and enrollment keys; this selects the agent kind.
The agent's identity, 1 to 64 characters matching [A-Za-z0-9_.:-]. Becomes its attribution user
id in the console.
Same slug rules. Rolls the agent's spend up to a team.
Caps the key's real out-of-pocket spend per UTC calendar month; included subscription usage
counts only its over-allowance overage. Omit, or pass 0, for an uncapped key. Negative values
clamp to 0.
For a shared agent that serves many people. When true, each request is attributed to the
user (and team, when sent) the agent names in x-anyray-metadata, validated as an id; a
value that fails validation falls back to the agent's own name. The agent stays on every spend
row as agent, and the people it names count as seats like any attributed user. The gateway
trusts what the key sends, so a mis-filed user is your own key mis-reporting; enable it only on
an agent you operate. Change it later with PATCH /admin/v1/keys/:id (null clears).
Mints an ingest-only key for the Claude telemetry receiver.
POST /otel/v1/* accepts it; every inference route refuses it with 401 key_telemetry_only, so
it can sit in Anthropic's admin console or Claude Code managed settings without letting its holder
spend an org provider key. Set at mint only. Pair it with delegatedAttribution so each export
files under its own user.
Binds this agent's traffic to one provider, independent of the org routing config. Must be a
provider this gateway can route to (capabilities.supportedProviders on
GET /admin/v1/providers); anything else returns 400. Omit for the org routing.
Which of that provider's named keys pays. Only valid alongside
routingProvider; omit for the provider's default key. An unknown key id returns 400, so a
mistake surfaces here rather than as 424s on the agent's traffic later.
Routes the agent across several upstream accounts instead of one. Mutually exclusive with
routingProvider / routingProviderKeyId, which the gateway derives from this config's first
target; sending both returns 400.
{
"mode": "loadbalance",
"targets": [
{ "provider": "bedrock", "providerKeyId": "ci-account", "weight": 3 },
{ "provider": "anthropic", "weight": 1 }
],
"retryAttempts": 2
}
mode is single, loadbalance, or fallback. targets holds 1 to 8 entries, each a
provider plus an optional providerKeyId; weight (a non-negative integer, default 1) is
read in loadbalance only, and fallback tries targets in order on 429, 500, 502, and
503. retryAttempts (0 to 5) retries each target on those statuses before moving on. Every
target is validated at write time: an unroutable provider or unknown key id anywhere in the list,
a duplicate target, a single mode with several targets, or an all-zero-weight loadbalance
returns 400.
Load balancing is sticky per conversation (each upstream account keeps its own prompt cache, so moving a conversation mid-flight would rewrite its whole prefix); the split happens across conversations. See the gateway's routing.
Gives the agent its own named key for one or
more providers, as provider id to key id. The agent's requests to a listed provider use that key
in place of the default key; unlisted providers keep theirs. Send it with routingProvider and
routingConfig or on its own.
{ "x-ai": "xai-agent-a" }
Each provider must be one this gateway can route to, and each key id one of that provider's named
keys (ids are case-sensitive); anything else returns 400. At most 32 entries. null or {}
clears it. A key picked in a route still wins: routingProviderKeyId, a routingConfig target's
providerKeyId, or a provider_key_id in the org routing config. A
key deleted after you set it fails the agent's requests to that provider with 424 rather than
falling back to the default key. Service keys only; a human key returns 400.
Setting it needs providerkeys:manage (the providers:write scope) as well, because the agent's key
then reaches every host the org routing sends that provider's default key to. Without it the mint
returns 403 agent_provider_keys_requires_providerkeys_manage, before any key id is checked.
Returns 201 with { key, record }, where key is the raw ark_svc_… token, returned only
here. A malformed body returns 400.
Change or clear a binding later with PATCH /admin/v1/keys/:id. {"routingProvider": null}
clears the whole binding, scalars and config alike, and the agent goes back to the org routing;
{"routingConfig": null} clears a multi-target config while leaving a single-provider binding
alone. Sending routingProvider replaces a stored multi-target config rather than editing
its first target, so an agent narrowed back to one provider stops billing the accounts you
removed. Sending routingProviderKeyId alone to a multi-provider agent returns 400 rather
than collapsing the split; send the full routingConfig, or a routingProvider with it.
routingProviderKeys is sent and stored on its own: changing the route fields leaves it alone,
so clear it with {"routingProviderKeys": null}. Changing or clearing it needs providers:write
too; a PATCH that omits it, or sends the stored map unchanged, does not. A binding applies to service keys only; one on a
human key returns 400.
routingRules on service-key mint or patch holds up to 16 model rules:
{ match: { model: "gpt-5" }, dest: { mode: "org" } } or a match.family rule.
Families are claude-opus, claude-sonnet, claude-haiku, claude, gpt-5, gpt,
gemini, and grok. Each destination is org or a { mode, targets, retryAttempts? }
agent routing document. Exact model ids are trimmed, 1 to 128 characters, using letters,
digits and ._:@/+[]- only. Duplicate models or families are refused. Agents cannot use subscription.
Patch with null or [] to clear rules; omit the field to keep them.
Rules are stored separately from the default. Request routing support ships separately.
Send the key on a raw HTTP request
A client that doesn't read the environment sends the key in a header. The gateway checks
x-anyray-api-key first, then Authorization: Bearer, then x-api-key:
curl https://<your-anyray-gateway>/v1/chat/completions \
-H "Authorization: Bearer ark_svc_…" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-6.1-sol", "messages": [{"role": "user", "content": "ping"}]}'
x-api-key works only because the token carries the ark_ prefix; that's what tells the
gateway it's ours. A non-ark_ x-api-key is treated as your own provider key (BYO-key mode)
and passed upstream untouched.
Manage keys
| Action | How |
|---|---|
| List every agent, with status and last active | The Agents card, or get /admin/v1/keys?type=service (never returns the raw token) |
| Read one | Click its row, or get /admin/v1/keys/:id |
| Change its routing | The row's General info tab, or patch /admin/v1/keys/:id |
| Revoke one | The row's Revoke, or delete /admin/v1/keys/:id |
Revoking marks the key rather than deleting it, so the agent's historical spend stays out of your seat count after it's retired. Changing a budget means minting a new key.
monthlyBudgetUsd is fail-open. Completed successful requests update the shared counter after
their responses, so concurrent requests can overshoot it. If the counter is unavailable, traffic
continues rather than stalling.