Primary navigation

Legacy APIs

Shell

Run shell commands in hosted containers or your own local runtime.

The shell tool gives models the ability to work inside a complete terminal environment. We support shell for local execution and for hosted execution through the Responses API.

The shell tool lets models run commands through either:

Shell is available through the Responses API. It’s not available via the Chat Completions API.

Running arbitrary shell commands can be dangerous. Always sandbox execution, apply allowlists or denylists where possible, and log tool activity for auditing.

Hosted shell quickstart

Hosted shell is a native and streamlined option for tasks that need richer, deterministic processing, from running calculations to working with multimedia.

Use container_auto when you want OpenAI to provision and manage a container for the request.

Shell tool with container_auto
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
curl -L 'https://api.openai.com/v1/responses' \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-5.2",
    "tools": [
      { "type": "shell", "environment": { "type": "container_auto" } }
    ],
    "input": [
      {
        "type": "message",
        "role": "user",
        "content": [
          { "type": "input_text", "text": "Execute: ls -lah /mnt/data && python --version && node --version" }
        ]
      }
    ],
    "tool_choice": "auto"
  }'

Hosted runtime details

  • Runtime is currently based on Debian 12 and may change over time.
  • Default working directory is /mnt/data.
  • /mnt/data is always present and is the supported path for user-downloadable artifacts.
  • Hosted shell doesn’t support interactive TTY sessions.
  • Hosted shell commands don’t run with sudo.
  • You can run services inside the container when your workflow needs them.

Current preinstalled languages include:

  • Python 3.11
  • Node.js 22.16
  • Java 17.0
  • PHP 8.2
  • Ruby 3.1
  • Go 1.23

Reuse a container across requests

If you need a long-running environment for iterative workflows, create a container and then reference it in subsequent Responses API calls.

1. Create a container

Create a reusable container
1
2
3
4
5
6
7
8
curl -L 'https://api.openai.com/v1/containers' \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "name": "analysis-container",
    "memory_limit": "1g",
    "expires_after": { "anchor": "last_active_at", "minutes": 20 }
  }'

2. Reference the container in Responses

Use shell with container_reference
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
curl -L 'https://api.openai.com/v1/responses' \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-5.2",
    "tools": [
      {
        "type": "shell",
        "environment": {
          "type": "container_reference",
          "container_id": "cntr_08f3d96c87a585390069118b594f7481a088b16cda7d9415fe"
        }
      }
    ],
    "input": "List files in the container and show disk usage."
  }'

Attach skills

Skills are reusable, versioned bundles that you can mount in hosted shell environments. This defines the available skills, and at shell execution time the model decides whether to invoke them.

Use the Skills guide for upload and versioning details.

Create a container with attached skills
1
2
3
4
5
6
7
8
9
10
curl -L 'https://api.openai.com/v1/containers' \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "name": "skill-container",
    "skills": [
      { "type": "skill_reference", "skill_id": "skill_4db6f1a2c9e73508b41f9da06e2c7b5f" },
      { "type": "skill_reference", "skill_id": "openai-spreadsheets", "version": "latest" }
    ]
  }'

Network access

Hosted containers don’t have outbound network access by default.

To enable it:

  1. An admin must configure your org allow list in the dashboard.
  2. You must explicitly set network_policy on the container environment in your request.
Shell tool with network allowlist
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
curl -L 'https://api.openai.com/v1/responses' \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.2",
    "tool_choice": "required",
    "tools": [
      {
        "type": "shell",
        "environment": {
          "type": "container_auto",
          "network_policy": {
            "type": "allowlist",
            "allowed_domains": ["pypi.org", "files.pythonhosted.org", "github.com"]
          }
        }
      }
    ],
    "input": [
      {
        "role": "user",
        "content": "In the container, pip install httpx beautifulsoup4, fetch release pages, and write /mnt/data/release_digest.md."
      }
    ]
  }'

Allowlisting domains introduces security risks such as prompt injection-driven data exfiltration. Only allowlist domains you trust and that attackers cannot use to receive exfiltrated data. Carefully review the Risks and safety section below before using this tool.

Network policy precedence

When multiple controls are present:

  • Your org allow list defines the full set of allowed_domains.
  • Request-level network_policy further restricts access.
  • Requests fail if allowed_domains includes domains outside your org allow list.

Domain secrets

Use domain_secrets when a domain in your allowed_domains list requires private authorization headers, such as Authorization: Bearer <token>.

Each secret entry includes:

  • Target domain
  • Friendly secret name
  • Secret value

At runtime:

  • The model and runtime see placeholder names (for example, $API_KEY) instead of raw credentials.
  • The auth-translation sidecar applies raw secret values only for approved destinations.
  • Raw secret values don’t persist on API servers and don’t appear in model-visible context.

This lets the assistant call protected services while reducing leakage risk.

Shell tool with domain_secrets
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
curl -L 'https://api.openai.com/v1/responses' \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.2",
    "input": [
      {
        "role": "user",
        "content": "Use curl to call https://httpbin.org/headers with header Authorization: Bearer $API_KEY. Tell me what you see in the final text response."
      }
    ],
    "tool_choice": "required",
    "tools": [
      {
        "type": "shell",
        "environment": {
          "type": "container_auto",
          "network_policy": {
            "type": "allowlist",
            "allowed_domains": ["httpbin.org"],
            "domain_secrets": [
              {
                "domain": "httpbin.org",
                "name": "API_KEY",
                "value": "debug-secret-123"
              }
            ]
          }
        }
      }
    ]
  }'

Multi-turn workflows

To continue work in the same hosted environment, reuse the container and pass previous_response_id.

Continue a shell workflow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
curl -L 'https://api.openai.com/v1/responses' \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-5.2",
    "previous_response_id": "resp_2a8e5c9174d63b0f18a4c572de9f64a1b3c76d508e12f9ab47",
    "tools": [
      {
        "type": "shell",
        "environment": {
          "type": "container_reference",
          "container_id": "cntr_f19c2b51e4a06793d82d54a7be0fc9154d3361ab28ce7f6041"
        }
      }
    ],
    "input": "Read /mnt/data/top5.csv and report the top candidate."
  }'

Download artifacts

Hosted shell can produce downloadable files. Use the same container/files APIs as code interpreter to retrieve artifacts written under /mnt/data.

Shell output in Responses

Hosted shell and local shell use the same output item types. Shell runs are represented by paired output items:

  • shell_call: commands requested by the model.
  • shell_call_output: command output and exit outcomes.
Example shell_call item
1
2
3
4
5
6
7
8
9
10
{
  "type": "shell_call",
  "call_id": "call_9d14ac6f2b73485e91c0f4da6e1b27c8",
  "action": {
    "commands": ["ls -l"],
    "timeout_ms": 120000,
    "max_output_length": 4096
  },
  "status": "in_progress"
}

Local shell mode

You can also run shell commands in your own local runtime by executing shell_call actions and sending shell_call_output back to the model.

Use this mode when you need full control over execution environment, filesystem access, or existing internal tooling.

Local shell request
1
2
3
4
5
6
7
8
9
curl -L 'https://api.openai.com/v1/responses' \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-5.2",
    "instructions": "The local bash shell environment is on Mac.",
    "input": "find me the largest pdf file in ~/Documents",
    "tools": [{ "type": "shell", "environment": { "type": "local" } }]
  }'

When you receive shell_call output items:

  • Execute requested commands in your runtime.
  • Capture stdout, stderr, and outcome.
  • Return results as shell_call_output in the next request.
Local shell executor example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@dataclass
class CmdResult:
    stdout: str
    stderr: str
    exit_code: int | None
    timed_out: bool

class ShellExecutor:
    def __init__(self, default_timeout: float = 60):
        self.default_timeout = default_timeout

    def run(self, cmd: str, timeout: float | None = None) -> CmdResult:
        t = timeout or self.default_timeout
        p = subprocess.Popen(
            cmd,
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
        )
        try:
            out, err = p.communicate(timeout=t)
            return CmdResult(out, err, p.returncode, False)
        except subprocess.TimeoutExpired:
            p.kill()
            out, err = p.communicate()
            return CmdResult(out, err, p.returncode, True)
Example shell_call_output payload
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
  "type": "shell_call_output",
  "call_id": "call_3ef1b8c79a4d6520f9e3ab7d41c68f25",
  "max_output_length": 4096,
  "output": [
    {
      "stdout": "...",
      "stderr": "...",
      "outcome": {
        "type": "exit",
        "exit_code": 0
      }
    },
    {
      "stdout": "...",
      "stderr": "...",
      "outcome": {
        "type": "timeout"
      }
    }
  ]
}

For legacy migration details, see the older Local shell guide.

Use local shell with Agents SDK

If you are using the Agents SDK, you can pass your own shell executor implementation to the shell tool helper.

Use local shell with Agents SDK
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import {
  Agent,
  run,
  withTrace,
  Shell,
  ShellAction,
  ShellResult,
  shellTool,
} from "@openai/agents";

class LocalShell implements Shell {
  async run(action: ShellAction): Promise<ShellResult> {
    return {
      output: [
        {
          stdout: "Shell is not available. Needs to be implemented first.",
          stderr: "",
          outcome: {
            type: "exit",
            exitCode: 1,
          },
        },
      ],
      maxOutputLength: action.maxOutputLength,
    };
  }
}

const shell = new LocalShell();

const agent = new Agent({
  name: "Shell Assistant",
  model: "gpt-5.2",
  instructions:
    "You can execute shell commands to inspect the repository. Keep responses concise and include command output when helpful.",
  tools: [
    shellTool({
      shell,
      needsApproval: true,
      onApproval: async (_ctx, _approvalItem) => {
        return { approve: true };
      },
    }),
  ],
});

await withTrace("shell-tool-example", async () => {
  const result = await run(agent, "Show the Node.js version.");
  console.log(`\nFinal response:\n${result.finalOutput}`);
});

You can find working examples in the SDK repositories.

Shell tool example - TypeScript

TypeScript example for the shell tool in the Agents SDK.

Shell tool example - Python

Python example for the shell tool in the Agents SDK.

Handling common errors

  • If a command exceeds your execution timeout, return a timeout outcome and include partial captured output.
  • If max_output_length is present on shell_call, include it in shell_call_output.
  • Don’t rely on interactive commands; shell tool execution should be non-interactive.
  • Preserve non-zero exit outputs so the model can reason about recovery steps.

Risks and safety

Enabling network access in the Containers API is a powerful capability, and it introduces meaningful security and data-governance risk. By default, network access is not enabled. When enabled, outbound access should remain tightly scoped to trusted domains needed for the task.

Network-enabled containers can interact with third-party services and package registries. That creates risks including data exfiltration, prompt-injection-driven tool misuse, and accidental access beyond intended boundaries. These risks increase when policies are broad, static, or inconsistently enforced.

Understand prompt injection risks from network-retrieved content

Any external content fetched over the network may contain hidden instructions intended to manipulate model behavior. Treat untrusted network content as potentially adversarial, and require additional caution for actions that can modify data or systems.

Connect only to trusted destinations

Allow only domains you trust and actively maintain. Be cautious with intermediaries and aggregators that proxy to other services, and review their data handling and retention practices before allowlisting.

Build in reviews before and after requests are executed

Review the shell tool command and execution output, which are provided in the Responses API response. Capture requested hosts and actual outbound destinations for each session. Periodically review logs to verify access patterns match expectations, detect drift, and identify suspicious behavior.

Validate data residency and retention requirements

OpenAI data controls apply within OpenAI boundaries. However, data transmitted to third-party services over network connections is subject to their data retention policies. Ensure external endpoints meet your residency, retention, and compliance requirements.