Skip to main content

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.

StatusMeaning
Checking…The keys or health read is still in flight.
Not connectedNo request has authenticated with this key.
ActiveAt 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 missingOne runtime is wired and another is not. The pill names both. Clicking the row opens Setup at step 3.
MCP disconnectedNo 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 callsEvery request the key sent in the last day forbade tool calls, so there is nothing to retrieve and nothing to wire.
Active · forwarded unchangedEvery 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).
ExpiredexpiresAt has passed.
RevokedRevoked. 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:

VerdictMeaning
wiredThe runtime declared the retrieval tool or the gateway's /mcp, the gateway served retrieval for it, or a deferred catalog latched.
unlatchedThe 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.
unwiredRequests 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.
parityRequests 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_onlyEvery request forbade tool calls. Nothing to wire.
unknownRequests 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.

readPathCard readsMeaning
declaredRead path readyThe app tool loop declared anyray_retrieve, and harnessRetrieval is off. Turning it on lets the gateway trim this loop.
absentNo read path, finish step 2The loop declares no anyray_retrieve, and its class is not opted into served retrieval.
partialRead path on some steps, finish step 2Some requests declared it and some did not.
The field is missingForwarded unchangedThe 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 runtimeMeaning
loadedA request listed the skill to the model.
not_loadedRequests could list skills, and none listed anyray. Install it where the app's skill loader reads it.
no_loaderThe loop has no skill loader, such as a plain Messages loop or LangChain. Nothing to install.
unknownOnly requests the gateway cannot judge.
invokedMeaning
currentThe newest recorded load used the body this gateway serves now.
outdatedThe newest recorded load used a body other than the body this gateway serves now.
neverThe runtime could load the skill, but no request invoked it.
unknownNo 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.

LineMeaning
Needs gateway upgradeThis gateway predates read-back. The host is fine.
Not availableThe gateway could not read its spend store.
No handles yetNothing was minted or read in the window.
<tool>: N handles, none read yetNormal. The model had no need for the trimmed content.
<tool>: N handles, M read backReads resolve.
<tool>: N handles, M read missedA few reads missed, too few to call a failure.
<tool>: M read back, handles not countableOlder rows in the window hide the minted count.
<tool>: reads failing3 or more misses, and at least half of all reads missed.
<tool>: served retrieval failingGateway-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 read3 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:

clientToolsMeaning
The field is missingThis 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).

Mint a service key
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"
}'
typestringrequired

service. The same route mints user and enrollment keys; this selects the agent kind.

namestringrequired

The agent's identity, 1 to 64 characters matching [A-Za-z0-9_.:-]. Becomes its attribution user id in the console.

teamstringrequired

Same slug rules. Rolls the agent's spend up to a team.

monthlyBudgetUsdnumber

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.

delegatedAttributionboolean

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).

telemetryOnlyboolean

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.

routingProviderstring

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.

routingProviderKeyIdstring

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.

routingConfigobject

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.

Load balance across two accounts
{
"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.

routingProviderKeysobject

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.

This agent's xAI requests bill xai-agent-a
{ "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:

One request
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​

ActionHow
List every agent, with status and last activeThe Agents card, or get /admin/v1/keys?type=service (never returns the raw token)
Read oneClick its row, or get /admin/v1/keys/:id
Change its routingThe row's General info tab, or patch /admin/v1/keys/:id
Revoke oneThe 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.

The budget is a soft cap

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.