Skip to main content

OpenClaw reference

The depth behind the OpenClaw guide. OpenClaw picks a model ref; the gateway picks the provider and the model id that reaches it.

Routing examples

Most orgs need exactly one config: dispatch by requested model. Machines carry a plain model menu; the gateway decides the vendor. Set it on the console Routing page, or PUT /admin/routing-config:

Dispatch by requested model, across vendors
{
"strategy": {
"mode": "conditional",
"conditions": [
{ "query": { "params.model": { "$regex": "grok" } }, "then": "oci-grok" },
{ "query": { "params.model": { "$regex": "^gpt" } }, "then": "gpt" },
{ "query": { "params.model": { "$regex": "^claude-opus" } }, "then": "opus" },
{ "query": { "params.model": { "$regex": "^claude" } }, "then": "sonnet" }
],
"default": "sonnet"
},
"targets": [
{ "name": "oci-grok", "provider": "oracle",
"override_params": { "model": "xai.grok-4.3" } },
{ "name": "gpt", "provider": "openai",
"override_params": { "model": "gpt-5.5" } },
{ "name": "opus", "provider": "anthropic",
"override_params": { "model": "claude-opus-5" } },
{ "name": "sonnet", "provider": "anthropic",
"override_params": { "model": "claude-sonnet-5" } }
]
}

The other modes:

Single: everything to Oracle
{
"strategy": { "mode": "single" },
"targets": [{ "provider": "oracle" }]
}

Every provider slug above is one Anyray serves directly (oracle, x-ai, anthropic, openai, bedrock, vertex-ai, google, groq, deepseek, nebius, tensormesh, openrouter, and more). Each target draws its credential from the server-held store; Oracle needs no api_key in the config (region and credential come from the provider bag, and IAM signing has no key at all). A client can override per request with the x-anyray-config header. Full semantics: Configure → Routing.

A cross-vendor fallback must rewrite the model

Model ids do not transfer between vendors: xai.grok-4.3 is an OCI id, grok-4.3 is xAI's own, and neither means anything to Anthropic. A fallback chain without override_params.model on each target fails at the second hop with an upstream 404 rather than failing over.

Rules that decide whether these behave as written:

  • First match wins; the specific condition goes first. ^claude-opus must precede ^claude. The Grok arm matches grok unanchored because ^grok would miss OCI's xai.grok-4.3.
  • Identity fields evaluate on the verified key-bound identity. metadata.user and metadata.team cannot be steered via x-anyray-metadata; a team lane needs team-bearing keys (SSO enrollment). Other metadata.<key> fields stay client-controlled, and params.<field> reads the request body.
  • Routing applies to API-key traffic only. passthrough subscription traffic keeps the user's own token and provider.
  • A sole configured provider key is already the default: a single-provider deployment can skip routing config entirely.

Two layers of routing

OpenClaw configGateway routing
Lives inopenclaw.json, on the machineConsole Routing / PUT /admin/routing-config, server-side
DecidesWhich provider entry and model ref a conversation usesWhich upstream provider serves it, and under which model id
Changed byEditing the file (or /model), per machineAn admin, once, for every client at once
Holds credentialsThe ark_… client key onlyThe provider keys (Oracle, xAI, Anthropic)
SeesNothing about upstreamsFallback, load balance, conditions, retry

Put in openclaw.json only what a user would legitimately choose (a fast model versus a frontier one); leave which vendor serves that choice to the gateway.

Precedence, exactly

An x-anyray-provider header on a provider entry wins over the org's routing config, and an x-anyray-config header wins over both. With neither, the gateway applies the default routing config; if none is stored and exactly one provider key is configured, that provider is used. A container that sends no Anyray routing headers is fully steered by the console.

Stable aliases

Register aliases centrally (console, or PUT /admin/model-aliases) and the Stable aliases variant becomes a config no vendor id ever appears in: re-pointing frontier at another vendor is one admin change, and no machine is edited.

PUT /admin/model-aliases
{
"frontier": "oracle/xai.grok-4.3",
"fast": "oracle/openai.gpt-oss-120b",
"claude": "anthropic/claude-sonnet-5"
}

An alias resolves only when the client sent no explicit provider (the gateway rewrites the body model and names the resolved provider), so aliases and the Two entries variant are mutually exclusive: a pinned x-anyray-provider wins and the alias is ignored. contextWindow and maxTokens still describe whatever the alias points at, so re-pointing across a large context-window gap means revisiting those numbers.

Config variants

Ready-made openclaw.json shapes. All assume the gateway-side setup from the guide; which vendors can share one entry is explained there, under Adding more vendors.

These vary the models block only. Keep the guide's mcp.servers.anyray block alongside whichever you pick: routing without it leaves most optimization held back (why).

Grok direct from xAI

Grok direct from xAI
{
models: {
providers: {
anyray: {
baseUrl: 'https://gateway.example.anyray.ai',
api: 'anthropic-messages',
apiKey: '${ANYRAY_CLIENT_KEY}',
headers: {
'x-anyray-api-key': '${ANYRAY_CLIENT_KEY}',
'x-anyray-metadata': '{"tool":"openclaw"}',
},
// xAI's own ids, with NO dotted prefix. Same models as OCI serves,
// different namespace and a different provider key on the gateway.
models: [
{
id: 'grok-4.6',
name: 'Grok 4.6',
reasoning: true,
input: ['text'],
contextWindow: 500000,
maxTokens: 128000,
},
{
id: 'grok-build-0.1',
name: 'Grok Build 0.1 (cheap coding)',
reasoning: true,
input: ['text'],
contextWindow: 256000,
maxTokens: 128000,
},
],
},
},
},
agents: {
defaults: {
model: { primary: 'anyray/grok-4.6', fallbacks: ['anyray/grok-build-0.1'] },
},
},
}

Claude Opus and Sonnet

Claude Opus and Sonnet
{
models: {
providers: {
anyray: {
baseUrl: 'https://gateway.example.anyray.ai',
api: 'anthropic-messages',
apiKey: '${ANYRAY_CLIENT_KEY}',
headers: {
'x-anyray-api-key': '${ANYRAY_CLIENT_KEY}',
'anthropic-beta': 'interleaved-thinking-2025-05-14',
'x-anyray-metadata': '{"tool":"openclaw"}',
},
// maxTokens is the model's OUTPUT ceiling, and it differs by family:
// 32000 for opus, 64000 for sonnet. Over-stating it is a 400.
models: [
{
id: 'claude-opus-5',
name: 'Claude Opus 5',
reasoning: true,
input: ['text', 'image'],
contextWindow: 1000000,
maxTokens: 32000,
},
{
id: 'claude-sonnet-5',
name: 'Claude Sonnet 5',
reasoning: true,
input: ['text', 'image'],
contextWindow: 1000000,
maxTokens: 64000,
},
],
},
},
},
agents: {
defaults: {
// Sonnet primary, Opus for the work that needs it (`/model
// anyray/claude-opus-5` per conversation).
model: {
primary: 'anyray/claude-sonnet-5',
fallbacks: ['anyray/claude-opus-5'],
},
// Never "none": that strips every cache_control marker, so each warm
// turn re-reads the whole prefix at full price.
params: { cacheRetention: 'short' },
},
},
}

Route these to anthropic or bedrock, never Oracle: the OCI translation loses cache_control and thinking blocks, so a warm Claude session loses its prompt cache (Grok on OCI is unaffected). Never set cacheRetention: "none"; unset resolves to "short".

GPT (needs its own entry)

GPT (needs its own entry)
{
models: {
providers: {
// GPT CANNOT share an anthropic-messages entry with Claude or Grok. The
// gateway's `openai` provider serves no /v1/messages endpoint, so a GPT
// id sent on that wire does not route. It needs this second entry, on
// the OpenAI wire, with the `/v1` base that transport expects.
'anyray-gpt': {
baseUrl: 'https://gateway.example.anyray.ai/v1',
api: 'openai-completions',
apiKey: '${ANYRAY_CLIENT_KEY}',
headers: {
'x-anyray-api-key': '${ANYRAY_CLIENT_KEY}',
'x-anyray-provider': 'openai',
'x-anyray-metadata': '{"tool":"openclaw"}',
},
models: [
{
id: 'gpt-5.5',
name: 'GPT-5.5',
reasoning: true,
input: ['text', 'image'],
contextWindow: 1050000,
maxTokens: 65536,
},
],
},
},
},
agents: {
defaults: { model: { primary: 'anyray-gpt/gpt-5.5' } },
},
}

Everything: Opus, Sonnet, Grok, GPT

Requires Dispatch by requested model from Routing examples.

Everything: Opus, Sonnet, Grok, GPT
{
models: {
providers: {
// TWO entries, because the wires differ. Claude and Grok share the
// anthropic-messages entry (the gateway translates Grok to xAI's chat
// path); GPT needs the OpenAI wire, since `openai` serves no
// /v1/messages. Both point at the same gateway and the same key.
//
// REQUIRES the "Dispatch by requested model" routing config. This
// entry pins no provider, and the gateway has no model->provider map, so
// without that config a Claude id and a Grok id are indistinguishable to
// it and both go wherever the default points.
anyray: {
baseUrl: 'https://gateway.example.anyray.ai',
api: 'anthropic-messages',
apiKey: '${ANYRAY_CLIENT_KEY}',
headers: {
'x-anyray-api-key': '${ANYRAY_CLIENT_KEY}',
'anthropic-beta': 'interleaved-thinking-2025-05-14',
'x-anyray-metadata': '{"tool":"openclaw"}',
},
models: [
{
id: 'claude-sonnet-5',
name: 'Claude Sonnet 5',
reasoning: true,
input: ['text', 'image'],
contextWindow: 1000000,
maxTokens: 64000,
},
{
id: 'claude-opus-5',
name: 'Claude Opus 5',
reasoning: true,
input: ['text', 'image'],
contextWindow: 1000000,
maxTokens: 32000,
},
{
id: 'xai.grok-4.3',
name: 'Grok 4.3 (OCI)',
reasoning: true,
input: ['text'],
contextWindow: 1000000,
maxTokens: 128000,
},
],
},
'anyray-gpt': {
baseUrl: 'https://gateway.example.anyray.ai/v1',
api: 'openai-completions',
apiKey: '${ANYRAY_CLIENT_KEY}',
headers: {
'x-anyray-api-key': '${ANYRAY_CLIENT_KEY}',
'x-anyray-provider': 'openai',
'x-anyray-metadata': '{"tool":"openclaw"}',
},
models: [
{
id: 'gpt-5.5',
name: 'GPT-5.5',
reasoning: true,
input: ['text', 'image'],
contextWindow: 1050000,
maxTokens: 65536,
},
],
},
},
},
agents: {
defaults: {
model: {
primary: 'anyray/claude-sonnet-5',
fallbacks: ['anyray/xai.grok-4.3'],
},
params: { cacheRetention: 'short' },
},
},
}

Stable aliases, no vendor ids

Register the aliases first: Stable aliases.

Stable aliases, no vendor ids
{
models: {
providers: {
anyray: {
baseUrl: 'https://gateway.example.anyray.ai',
api: 'anthropic-messages',
apiKey: '${ANYRAY_CLIENT_KEY}',
headers: {
'x-anyray-api-key': '${ANYRAY_CLIENT_KEY}',
'anthropic-beta': 'interleaved-thinking-2025-05-14',
'x-anyray-metadata': '{"tool":"openclaw"}',
},
// These ids are ALIASES registered on the gateway, not vendor models.
// Re-pointing `frontier` at another vendor is an admin change, and no
// machine is edited. See "Stable aliases" above.
models: [
{
id: 'frontier',
name: 'Frontier',
reasoning: true,
input: ['text'],
contextWindow: 1000000,
maxTokens: 128000,
},
{
id: 'fast',
name: 'Fast',
input: ['text'],
contextWindow: 131072,
maxTokens: 32000,
},
],
},
},
},
agents: {
defaults: {
model: { primary: 'anyray/frontier', fallbacks: ['anyray/fast'] },
params: { cacheRetention: 'short' },
},
},
}

Two entries, one provider each

Two entries, one provider each
{
models: {
providers: {
// Each entry names its gateway provider outright, so the model picker
// shows the vendor. An explicit provider header WINS over the org's
// routing config, so these two bypass it.
'anyray-oci': {
baseUrl: 'https://gateway.example.anyray.ai',
api: 'anthropic-messages',
apiKey: '${ANYRAY_CLIENT_KEY}',
headers: {
'x-anyray-api-key': '${ANYRAY_CLIENT_KEY}',
'x-anyray-provider': 'oracle',
'x-anyray-metadata': '{"tool":"openclaw"}',
},
models: [
{
id: 'xai.grok-4.3',
name: 'Grok 4.3 (OCI)',
reasoning: true,
input: ['text'],
contextWindow: 1000000,
maxTokens: 128000,
},
],
},
'anyray-claude': {
baseUrl: 'https://gateway.example.anyray.ai',
api: 'anthropic-messages',
apiKey: '${ANYRAY_CLIENT_KEY}',
headers: {
'x-anyray-api-key': '${ANYRAY_CLIENT_KEY}',
'x-anyray-provider': 'anthropic',
'anthropic-beta': 'interleaved-thinking-2025-05-14',
'x-anyray-metadata': '{"tool":"openclaw"}',
},
models: [
{
id: 'claude-sonnet-5',
name: 'Claude Sonnet 5',
reasoning: true,
input: ['text', 'image'],
contextWindow: 200000,
maxTokens: 64000,
},
],
},
},
},
agents: {
defaults: {
model: { primary: 'anyray-oci/xai.grok-4.3' },
params: { cacheRetention: 'short' },
},
},
}

Which OCI models to list

OCI fronts four vendors behind one provider slug; every model keeps OCI's dotted vendor prefix on the wire.

xAI Grok and OpenAI open-weights. Priced per million tokens, so they convert exactly.

Model idContextPriced
xai.grok-4.31MExact
xai.grok-4.201MExact
xai.grok-4.21MExact
xai.grok-4-1-fast2MExact
openai.gpt-oss-120b131kExact
openai.gpt-oss-20b128kExact

Meta Llama and Cohere Command. Route and meter normally, priced at a fallback rate. The OCI model reference is authoritative; any id it lists routes.

Model idPriced
meta.llama-4-scout-17b-16e-instructFallback rate
meta.llama-3.3-70b-instructFallback rate
meta.llama-3.1-405b-instructFallback rate
cohere.command-a-03-2025Fallback rate
cohere.command-a-visionFallback rate
cohere.command-r-08-2024Fallback rate

To list one, copy any variant above and swap the id; take contextWindow and maxTokens from Oracle's model reference (maxTokens must be positive or the Messages transport refuses to run; an over-stated contextWindow packs a prompt the model rejects).

Three OCI pricing and id caveats
  • These ids look like Bedrock and Vertex ids, and are not. OCI's id is a prefix of theirs: Vertex serves meta.llama-3.3-70b-instruct-maas, Bedrock cohere.command-r-plus-v1:0. Same family, different upstream, different price. Send an OCI id only to the oracle provider.
  • Meta and Cohere spend is indicative, not exact. Oracle bills those families per character with no published characters-per-token ratio, so Anyray prices them at a fallback rate (each row records it as priceSource). Oracle's invoice is authoritative.
  • Only Grok caches on OCI. Oracle publishes a cached-input rate for the Grok ids (warm prefixes price at the read tier) and no automatic prompt caching for meta.llama-*, cohere.*, or openai.gpt-oss-*, so cache-busting strategies stay free on those.

Other ways to connect

anyray-connect (unsandboxed machines)

Where you can install into the machine, Connect writes the whole config:

curl -fsSL https://app.anyray.ai/connect.sh | sh -s -- --gateway http://<gateway>:8787 --tools openclaw

Use the installer, not npx. Connect registers the retrieval MCP server below only when its own binary is durable, and npm runs npx from a temporary cache, so that form routes OpenClaw and then reports Skipped the Anyray retrieval MCP server.

Enroll first with --enroll <link> (or --sso). --dry-run previews. Connect merges Anyray-owned keys into your existing config and writes:

  • models.providers.anthropic: the gateway origin as baseUrl, api: "anthropic-messages", and a maxTokens value; models.providers.openai: http://<gateway>:8787/v1.
  • Your personal ark_… key on apiKey and the x-anyray-api-key header.
  • A default model only if you have none: primary anthropic/claude-sonnet-4-5, fallback anthropic/claude-haiku-4-5 when fallbacks is absent. An unrouted primary or fallback (say openrouter/…) is kept but reported: it bypasses the gateway.
  • agents.defaults.params.cacheRetention: "short" unless you already set a value, plus the beta headers OpenClaw drops on a custom host.
  • mcp.servers.anyray: the retrieval server (anyray_retrieve, anyray_recall) as a local stdio command, so a trim the optimizer replaced with a marker can be read back. Restart the gateway, or run openclaw mcp reload, to connect it. An anyray server you already have is left alone and reported. Container installs register the remote endpoint instead, since there is no binary to launch.

--revert removes only what Connect wrote; OpenClaw hot-reloads model config. Run openclaw config validate to confirm.

The Anyray plugin

For per-conversation sessions, plus a model menu your admin config drives. Connect installs the Anyray plugin (@anyray/openclaw-plugin) at ~/.anyray/openclaw-plugin, registered under plugins.load.paths and plugins.entries.anyray. It contributes the provider entry, stamps an opaque session id per call, and sets cache retention in code.

The plugin asks your gateway which models to show (GET /v1/me/models, plugin ≥ 0.4.0). It lists every alias you registered plus every model a conditional routing lane names outright, so adding a provider is a console action: store the key, add the lane or the alias, and the model reaches every host's picker with nothing edited on the machine and the key never leaving the gateway. A gateway that cannot answer (older build, unreachable) leaves the plugin's built-in list in place, so the picker is never empty.

OpenClaw caches the resolved list per agent and does not re-read it on a restart, so the new model lands when Connect next applies (the fleet drift watcher does this unattended, and anyray-connect apply forces it). Both paths clear that cache for you.

A $regex lane contributes nothing to the menu — a regex matches ids, it cannot list them — so publish the menu through aliases and keep the regex for dispatch:

Add them on the provider's card under ProvidersModels, or with PUT /admin/model-aliases:

PUT /admin/model-aliases
{ "grok-4.6": "x-ai/grok-4.6", "gpt-5.5": "openai/gpt-5.5" }

Giving each OpenClaw host its own upstream key

Two OpenClaw hosts can share one account for most models and hold their own for one. Mint an agent per host on the Agents page, then on Routing add a lane per host with And also set to the model that must stay separate. Everything else falls through to the base strategy:

MatchAnd alsoProviderKey
agent id svc_amodel grok-4.6x-aixai-agent-a
agent id svc_bmodel grok-4.6x-aixai-agent-b
(base strategy)bedrockshared

The user picks from the same menu on every host. Grok bills that host's own xAI key; Opus matches no lane and lands on the shared Bedrock account.

The agent identity comes from the client key the plugin sends, so each host must be enrolled with its own agent key. Two hosts sharing one key are one agent to the gateway, and both lanes collapse onto whichever matches first.

Add the narrow lane before the broad one

First match wins, and lanes cannot be reordered on the page. A plain agent id svc_a lane above the narrowed one swallows its traffic, so the separate key never bills. The page flags this, but adding them in order avoids it.

The plugin's built-in list (the fallback) carries Claude ids plus xAI-direct Grok ids (grok-4.6, grok-4.3, grok-build-0.1), naming the x-ai provider on those calls itself (not OCI's xai.grok-4.3, served by oracle). A model your gateway listed is never stamped: your stored routing resolves it, which is what carries a provider_key_id pin.

To govern the built-in Grok ids by your routing config too (plugin ≥ 0.3.0), set gatewayRouting: true in the plugin's config block; required for named-key pinning (provider_key_id) and conditional dispatch. Add the routing rule first, then flip the flag: an unstamped Grok request with no matching rule lands on your default provider and fails there. The usual rule:

{ strategy: { mode: 'conditional',
conditions: [ { query: { 'params.model': { $regex: '^grok-' } }, then: 'xai' } ],
default: 'main' },
targets: [ { name: 'xai', provider: 'x-ai', provider_key_id: 'team-research' },
{ name: 'main', provider: 'anthropic' } ] }

Restart the OpenClaw gateway once after the first install; config-only changes hot-reload. Run openclaw config validate and pick anyray/* models. Source: anyrayHQ/openclaw-plugin, MIT; Connect ships an embedded copy.

Requires OpenClaw 2026.7.1 or newer

The plugin imports plugin-sdk/provider-entry, which landed in 2026.7.1. On older builds the import fails at load and the gateway logs the plugin as unavailable; models.providers entries still work there.

Upgrade past 0.1.x if you installed from npm

0.1.1 and earlier stamped every request as {"tool":"openclaw"}, dropping the configured user and team, so that traffic reached the spend store unattributed. 0.2.0 sends the configured values. Connect-installed copies were never affected.

Claude subscription stays native

OpenClaw can ride a personal Claude subscription (a pasted setup token, or its claude-cli runtime). Connect holds a detected Claude seat on that native lane; pass --org --tools openclaw to move it onto the org API bill. A seat appearing on a machine Connect had already routed removes the stale gateway route.

Seat detection reads the machine's Claude sign-in state, not OpenClaw's credential store: a setup token pasted only into OpenClaw is invisible to Connect. In that case skip openclaw in the sweep (or accept that routing it moves that usage to org billing).

Troubleshooting

What you seeCause
Requests 404, or the optimizer never firesNo explicit api: 'anthropic-messages', so OpenClaw served the entry over openai-completions.
Boot fails with expected array, received undefinedA customized provider entry must list its own models; it no longer inherits OpenClaw's catalog.
Nothing routes, no errorsRouting is inert until a model ref resolves to the provider. Set agents.defaults.model.primary, or /model anyray/xai.grok-4.3.
401 naming an Oracle credentialNo Oracle credential stored on the gateway. It fails closed rather than forwarding your ark_ key upstream.
400 naming oracleRegionThe region is mandatory and has no default.
A new optimizer session mid-conversationSwitching model or thinking level changes OpenClaw's own Runtime line, which the session fingerprint covers. Costs one re-decided prefix, nothing after.
openclaw mcp probe anyray --json does not show both retrieval toolsThe server is unavailable or discovery returned an empty catalog. Run openclaw mcp reload, probe for anyray_retrieve and anyray_recall again, then run openclaw gateway restart if either is still absent. Anyray treats affected turns as no_discovery and keeps reversible trimming off.