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:
- Hosted shell containers managed by OpenAI.
- A local shell runtime that you host and execute yourself.
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.
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 12and may change over time. - Default working directory is
/mnt/data. /mnt/datais 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
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
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.
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:
- An admin must configure your org allow list in the dashboard.
- You must explicitly set
network_policyon the container environment in your request.
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_policyfurther restricts access. - Requests fail if
allowed_domainsincludes 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.
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.
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.
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.
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_outputin the next request.
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)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.
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.
TypeScript example for the shell tool in the Agents SDK.
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_lengthis present onshell_call, include it inshell_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.