> ## Documentation Index > Fetch the complete documentation index at: https://openrouter.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Shell > Give any model a sandboxed hosted shell on the Responses and Messages APIs export const Template = ({children, data}) => { const replace = s => s.replace(/\{\{(\w+)\}\}/g, (_, k) => (k in data) ? data[k] : `{{${k}}}`); const leafText = node => typeof node === 'string' ? node : node?.$$typeof && typeof node.props?.children === 'string' ? node.props.children : null; const collapseTokens = nodes => { const out = []; let i = 0; while (i < nodes.length) { const ta = leafText(nodes[i]); const tb = leafText(nodes[i + 1]); const tc = leafText(nodes[i + 2]); if (ta != null && tb != null && tc != null) { const m = (ta + tb + tc).match(/^([\s\S]*)\{\{(\w+)\}\}([\s\S]*)$/); if (m && (m[2] in data)) { out.push(m[1] + data[m[2]] + m[3]); i += 3; continue; } } out.push(nodes[i]); i++; } return out; }; const process = node => { if (typeof node === 'string') return replace(node); if (Array.isArray(node)) return collapseTokens(node.map(process)); if (node && typeof node === 'object') { if (node.$$typeof) return { ...node, props: process(node.props) }; return Object.fromEntries(Object.entries(node).map(([k, v]) => [k, process(v)])); } return node; }; return <>{process(children)}; }; export const API_KEY_REF = ''; Beta **Beta** Server tools are currently in beta. The API and behavior may change. The shell tool is available on the global endpoint (`openrouter.ai`) only. Requests through the [in-region endpoints](/docs/guides/privacy/provider-logging#enterprise-in-region-routing) (`eu.openrouter.ai`, `us.openrouter.ai`) are rejected. **Responses and Messages APIs only** The shell server tool is available through the [Responses API](/docs/api_reference/responses/overview) and the [Messages API](/docs/api/api-reference/anthropic-messages/create-a-message). Requesting it on the Chat Completions API returns a `400` error. The two APIs surface a shell run differently. On the Responses API the call becomes an `openrouter:shell` output item (or a native `shell_call` when you send OpenAI's tool shape). On the Messages API it becomes a `server_tool_use` content block named `openrouter:shell`, paired with an `openrouter_shell_tool_result` block carrying each command's output. Anthropic defines no native shell result block, so this OpenRouter-namespaced one is where the output arrives. The `openrouter:shell` server tool gives a model a hosted shell: a sandbox-backed clone of OpenAI's hosted `shell` tool that works with any model. When the model needs to run commands, it emits a shell call; OpenRouter executes the commands server-side in an isolated Linux container and returns each command's `stdout`, `stderr`, and exit or timeout outcome. Unlike the [Bash](/docs/guides/features/server-tools/bash) tool, the shell tool has no client-side execution mode: commands always run in a hosted environment, either OpenAI's native shell or OpenRouter's sandbox. ## How It Works 1. You include `{ "type": "openrouter:shell" }` in your `tools` array on a Responses or Messages API request. On the Responses API you can also send OpenAI's native `shell` tool shape; on non-OpenAI models it is routed to the OpenRouter sandbox automatically. 2. Based on the prompt, the model decides to run one or more shell commands and emits a shell call. 3. OpenRouter executes the commands in order, each in its own invocation, inside a sandboxed container. 4. Each command's `stdout`, `stderr`, and outcome (exit code or timeout) are returned to the model. 5. The model incorporates the results and may run further command batches in the same request. ## Quick Start ## Configuration The shell tool accepts optional `parameters` to choose its execution engine and environment: ```json lines theme={null} { "type": "openrouter:shell", "parameters": { "engine": "openrouter", "environment": { "type": "container_auto" } } } ``` | Parameter | Type | Default | Description | | ------------- | ------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `engine` | string | `auto` | Which shell engine to use: `openrouter` runs commands server-side in the OpenRouter sandbox; `auto` keeps the provider's native hosted shell when available (OpenAI) and routes to the OpenRouter sandbox on other providers | | `environment` | object | `container_auto` | Execution environment. Use `{ "type": "container_auto" }` for an OpenRouter-managed ephemeral container, or `{ "type": "container_reference", "container_id": "..." }` to reuse an existing container. `local` environments are not supported. See [Containers](/docs/guides/features/containers) | Containers sleep after 5 minutes idle; each command renews the timer. This is not configurable — a legacy `sleep_after_seconds` parameter is accepted and ignored. See [Container lifetime](/docs/guides/features/containers#container-lifetime). Defaults and caps are server-enforced and may change while the tool is in beta: | Limit | Default | Maximum | | ------------------------------------------- | ------------------- | ------------------- | | `timeout_ms` per command | 120,000 (2 minutes) | 300,000 (5 minutes) | | `max_output_length` per stream, per command | 16,384 characters | 65,536 characters | | `commands` per call | — | 100 | A `timeout_ms` or `max_output_length` above the maximum is clamped to it. A call with more than 100 commands is rejected. ### Network Policy Containers have **no outbound internet access by default**. The container configuration objects accept a `network_policy` field: ```json lines theme={null} { "type": "openrouter:shell", "parameters": { "engine": "openrouter", "environment": { "type": "container_auto", "network_policy": { "type": "allowlist", "allowed_domains": ["pypi.org", "files.pythonhosted.org"] } } } } ``` | Policy | Behavior | | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | | `{ "type": "disabled" }` | No outbound internet access | | `{ "type": "allowlist", "allowed_domains": [...] }` | Outbound access restricted to hosts matching the listed hostnames or glob patterns (max 50) | | omitted | Defaults to `disabled` — no outbound internet access. For unrestricted egress, use an allowlist of `["*"]` | The policy is **fixed when a container starts**: sending a different `network_policy` to a warm container fails the request with a `409`. Do not try to change a running container's policy — send the same policy for the container's lifetime. Platform constraints for allowlisted traffic: * Only ports 80 and 443 are reachable. * DNS resolution is provided by the platform and cannot be overridden by container configuration. * Entries are lowercase hostnames or glob patterns — no schemes, paths, or ports. `*` matches any run of characters (`*.example.com`, `google.*.com`). An exact hostname does not cover its subdomains: `example.com` does not allow `api.example.com`; use `*.example.com` or list each hostname. * `pip install` needs both `pypi.org` and `files.pythonhosted.org` (or `*.pythonhosted.org`) in the allowlist. Requests to hosts outside the policy fail inside the container with a connection error (HTTP traffic sees a `520` status), which the model can read on `stderr` and react to. ### Call Arguments The model generates the call arguments, mirroring OpenAI's hosted shell `shell_call.action`: | Field | Type | Description | | ------------------- | --------- | -------------------------------------------------------------------------------------------------------- | | `commands` | string\[] | Shell commands to run, each in its own invocation, in order | | `timeout_ms` | integer | Maximum execution time in milliseconds applied to each command | | `max_output_length` | integer | Maximum characters returned per stream. `stdout` and `stderr` are each capped to this value, per command | ## OpenAI native shell tool On the Responses API you can also send OpenAI's native tool shape (`{ "type": "shell" }`, or the legacy Codex `local_shell`) instead of `openrouter:shell`. On OpenAI models this uses OpenAI's own hosted shell; on any other model, OpenRouter routes the call to its sandbox transparently. The response emits the native `shell_call` output item either way. ## Response Format The tool returns one entry per command, matching OpenAI's `shell_call_output.output[]`: ```json lines theme={null} { "output": [ { "stdout": "total 0\ndrwxr-xr-x 2 root root 40 Jun 1 12:00 .\n", "stderr": "", "outcome": { "type": "exit", "exit_code": 0 } } ] } ``` Each command's `outcome` is either `{ "type": "exit", "exit_code": }` or `{ "type": "timeout" }`. A non-zero exit code indicates the command failed; the error output is returned on `stderr` so the model can read and react to it. ## Pricing Sandbox time (commands run with `engine: "openrouter"`) is billed at **\$0.0001 per second**. The clock starts when a request first runs a sandbox command and stops when the response completes. A container that is idle between requests is not billed. A request that starts a new or sleeping container is billed a minimum of 30 seconds. Later requests that reuse the same warm container pay only their metered time. ## Security Shell execution is sandboxed by design: * Commands execute in an isolated container, not on OpenRouter infrastructure or your machine. With `container_auto` the container is ephemeral; with `container_reference` it persists across requests. * Containers are scoped per account and workspace, so they are never shared across tenants. * Execution time is bounded by `timeout_ms`, clamped to 5 minutes per command. * `stdout` and `stderr` are each truncated to `max_output_length`, a per-stream cap clamped to 65,536 characters. See [Configuration](#configuration) for the defaults. ## Next Steps * [Server Tools Overview](/docs/guides/features/server-tools). Learn about server tools * [Containers](/docs/guides/features/containers). How sandbox containers work * [Bash](/docs/guides/features/server-tools/bash). Sandboxed shell for the Anthropic Messages API * [Tool Calling](/docs/guides/features/tool-calling). Learn about user-defined tool calling