Source: OpenAI API documentation — Programmatic Tool Calling
Editorial note: This independently published companion preserves the guide’s technical structure and key examples in a condensed form. The official documentation is the canonical and most current reference.
Programmatic Tool Calling lets a model write and run JavaScript that coordinates its available tools. Instead of returning every intermediate result to the model, a generated program can run calls concurrently, apply loops and conditions, combine results, and emit a smaller final value.
This is especially useful for predictable multi-call stages or large tool outputs. In the Responses API, the application decides whether the capability is available and which tools may be called directly, programmatically, or both. Client-owned functions still run in the application. In the Agents API, Programmatic Tool Calling is enabled by default and the managed harness handles the agent loop.
Always check the current model page before enabling this feature.
Runtime environment
Every generated program runs in a fresh, isolated V8 runtime. It supports JavaScript and top-level await, but it is deliberately not Node.js. The runtime has:
- no package installation;
- no direct network access;
- no general-purpose filesystem;
- no subprocess execution;
- no console;
- no persistent JavaScript state between executions.
Programs reach external systems only through tools enabled in the request. They emit results with text(...) or image(...).
Responses API workflows can use Programmatic Tool Calling with Zero Data Retention without a persistent code container. ZDR must already be enabled for the organization or project; store: false creates a stateless continuation pattern but does not enable ZDR by itself. Retention eligibility depends on the complete request, including the model, tools, and external services.
When to use it
Programmatic orchestration works best when control flow is predictable and code can reduce several intermediate outputs into a compact result.
| Task shape | Recommended approach |
|---|---|
| One lookup or action | Direct tool call |
| Filter, join, rank, deduplicate, aggregate, or validate several results | Programmatic Tool Calling |
| Dependent calls with predictable data flow | Programmatic Tool Calling with explicit limits and failure behavior |
| Adaptive search or semantic evaluation after every result | Direct tool calling |
| Writes or approval-sensitive actions | Direct calls by default, preserving a clear authorization boundary |
| Final citation or native-artifact validation | Direct calls unless the program preserves and validates every required item |
The important boundary is judgment. Code is a good fit for deterministic coordination and reduction; the model should remain in the loop when each result changes the meaning of the next step.
Responses API configuration
Add the hosted programmatic_tool_calling tool and set allowed_callers on every tool that generated JavaScript may invoke.
| |
allowed_callers determines how a tool can be invoked:
| Value | Behavior |
|---|---|
Omitted or ["direct"] | Direct model calls only |
["programmatic"] | Calls only from a generated program |
["direct", "programmatic"] | Either route is allowed |
parameters defines the function’s input. If it returns predictable structured data, use output_schema to describe the JSON object encoded in function_call_output.output. Generated JavaScript can then access known fields reliably.
Supported tools
Programmatic callers are supported by:
- function and custom tools;
- MCP tools;
- Apply Patch;
- local and hosted shell;
- Code Interpreter.
An MCP tool’s require_approval policy may pause a program until approval is granted. Before putting an OpenAI-hosted tool inside a program, review its data-retention and security guidance.
Combining it with Tool Search
Tool Search remains a top-level Responses API tool; generated JavaScript cannot run it. Deferred function, custom, and MCP tools are unavailable to programs until the model has loaded them. The correct sequence is:
- Let the model search for and load the deferred tool.
- Start a later program that uses it through
tools.*. - Ensure the loaded definition permits the
programmaticcaller.
An already-running program cannot pause to discover a new deferred tool.
Route each workflow stage explicitly
If a function supports both direct and programmatic calls, vague instructions such as “use Programmatic Tool Calling efficiently” are not enough. Assign each route to a bounded stage:
| |
For example, a program can fetch inventory and demand concurrently, then emit only sku, available_units, requested_units, and the calculated shortage_units. Any inventory-changing action remains a direct, approval-gated call.
Define one clear handoff between the two routes. Avoid switching back and forth or repeating work. If a fallback exists, specify it once and cap retries.
Response items and execution relationships
The API still returns a normal Responses object. Programmatic execution adds several possible top-level items to response.output:
program: generated JavaScript, itscall_id, and an opaquefingerprintused for replay or continuation.function_call: a tool invocation made by the program. It has its owncall_id;caller.caller_idpoints to the program call.program_output: the program’s emitted result and acompletedorincompletestatus. Itscall_idmatches the program.
| |
After the application returns both client-owned function results, a later response may contain:
| |
The JSON inside program_output.result follows the result shape requested in your instructions. The outer program_output follows the API contract. A final assistant message may arrive in the same or a later response, so the application must continue until it sees that message.
OpenAI runs the JavaScript. Your application executes client-owned function calls and returns each result as a function_call_output. Copy the function call’s caller field unchanged so the service can resume the correct program.
Continuation loop for client-owned functions
A program may pause several times. A robust application loop should:
- Send the request with the hosted capability and program-enabled tools.
- Execute every returned client-owned
function_call. - Return outputs with the original
call_idandcaller. - Handle incomplete responses.
- Continue if no pending function calls and no final message exist.
- Stop only when a final assistant message arrives.
This condensed JavaScript loop uses stateless continuation:
| |
With stored responses, use previous_response_id and send only new function outputs. With store: false, replay the complete ordered history: programs, reasoning items, function calls, outputs, and program outputs. Stateless reasoning-model workflows must also replay every reasoning item, including its encrypted content.
Designing program-friendly tools
- Return compact structured data that JavaScript can inspect without parsing prose.
- Define
output_schemaand document error behavior when the output shape is predictable. - Keep a tool direct if its output shape is unknown and requires model inspection.
- Specify the exact program result and the evidence it must retain.
- Return a structured failure when the program cannot produce a valid result.
- Make functions idempotent where possible; retries must not repeat unsafe side effects.
- Validate arguments and permissions inside the application for every call.
- Use specific tool names and descriptions.
- Require application-level approval for high-impact actions regardless of caller.
Evaluation
Programmatic Tool Calling can reduce how much intermediate data enters model context, but the benefit depends on the task and tool responses. Use direct calls as a baseline, then compare both modes on representative workloads.
Measure more than efficiency:
- final-answer correctness, completeness, and evidence coverage;
- input and total tokens, latency, and cost;
- model turns, tool calls, retries, and recovery;
- safety outcomes for side effects and approvals;
- whether the executed route matched the intended workflow stage.
Define the quality bar and required evidence before optimizing tokens. Any accepted quality tradeoff should be explicit.
Agents API
The Agents API runs Programmatic Tool Calling inside OpenAI’s managed agent harness and enables it by default. The harness gives the agent an exec tool and exposes existing tools to generated JavaScript; there is no need to wrap them as command-line programs.
Disable the feature explicitly with:
| |
Omitting the entry, omitting enabled, or using only { "type": "programmatic_tool_calling" } leaves it enabled.
Conversation-only sessions can use it with environment.type: "none". Tools that actually need a sandbox—such as Bash or executor MCPs—still require an execution environment.
JavaScript orchestration does not relocate tool execution. Shell commands still run in the sandbox, executor MCPs still use that environment, and function tools still call the application server. The agent receives their results and decides what should enter model context.