SPB Git

Repository Public

tools-programmatic-tool-calling.md 31.3 KB 826 lines markdown

1# Programmatic Tool Calling23> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.45Programmatic Tool Calling lets a model write and run JavaScript that coordinates its tools. A program can call tools in parallel, use loops and conditions, and keep intermediate results in the hosted runtime. This is useful when a task needs a sequence of related tool calls or needs to process large tool outputs before returning a result.67In the Responses API, your application decides whether Programmatic Tool Calling is available and which eligible tools the model can call directly, from a program, or either way. It continues to run any client-owned tool calls. The [Agents API](#agents-api) enables Programmatic Tool Calling by default and manages the agent loop for you.89Check the [model page](https://developers.openai.com/api/docs/models) before enabling Programmatic Tool Calling.1011## Understand the runtime environment1213OpenAI runs each generated program in a fresh, isolated V8 runtime. The runtime supports JavaScript with top-level `await`, but it does not provide Node.js, package installation, direct network access, a general-purpose filesystem, subprocess execution, a console, or persistent JavaScript state between program executions. Programs can interact with external systems only through tools enabled in the request and can emit output with `text(...)` or `image(...)`.1415For Responses API requests, Programmatic Tool Calling supports Zero Data Retention (ZDR) workflows without requiring a persistent code-execution container. ZDR must be enabled for the organization or project; setting `store: false` enables stateless continuation but does not enable ZDR by itself. Eligibility and retention depend on the complete request, including its model, tools, and third-party services; see [data controls](https://developers.openai.com/api/docs/guides/your-data).1617## Choose when to use Programmatic Tool Calling1819Use Programmatic Tool Calling when a stage has predictable control flow and code can return a smaller structured result. Use direct tool calling when one call is sufficient, each result requires fresh model judgment, or the work requires approval or preservation of citations or native artifacts.2021| Task shape                                                                                       | Recommended mode                                                                                                     |22| ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |23| A single lookup or action                                                                        | Use direct tool calling.                                                                                             |24| Several results that code can filter, join, rank, remove duplicates from, aggregate, or validate | Use Programmatic Tool Calling when the program can return a smaller structured result.                               |25| Dependent calls with predictable data flow                                                       | Use Programmatic Tool Calling when code can derive later arguments and the limits and failure behavior are explicit. |26| Adaptive search or semantic evaluation                                                           | Use direct tool calling when each result should influence the model's next decision.                                 |27| Writes or approval-sensitive actions                                                             | Use direct tool calling by default to preserve a clear authorization boundary.                                       |28| Final citation or native artifact validation                                                     | Use direct tool calling unless the program preserves the native output and validates every required item.            |2930## Configure Programmatic Tool Calling3132For the Responses API, add the `programmatic_tool_calling` hosted tool to the request. Then set `allowed_callers` on each eligible tool that the program can invoke.3334Enable Programmatic Tool Calling3536```json37[38  {39    "type": "function",40    "name": "get_inventory",41    "description": "Return an object with sku (string) and available_units (number).",42    "parameters": {43      "type": "object",44      "properties": {45        "sku": { "type": "string" }46      },47      "required": ["sku"],48      "additionalProperties": false49    },50    "output_schema": {51      "type": "object",52      "properties": {53        "sku": { "type": "string" },54        "available_units": { "type": "number" }55      },56      "required": ["sku", "available_units"],57      "additionalProperties": false58    },59    "allowed_callers": ["programmatic"]60  },61  {62    "type": "programmatic_tool_calling"63  }64]65```666768`allowed_callers` controls how the model can invoke a tool:6970| Value                        | Behavior                                                |71| ---------------------------- | ------------------------------------------------------- |72| Omitted or `["direct"]`      | The model can call the tool directly.                   |73| `["programmatic"]`           | Only code in a `program` item can call the tool.        |74| `["direct", "programmatic"]` | The model can call the tool directly or from a program. |7576`parameters` describes the function arguments. When a function returns predictable structured data, `output_schema` describes the JSON object encoded in its `function_call_output.output` string. Define both so generated JavaScript can use the returned fields reliably.7778### Supported tools7980The following tool types support `allowed_callers: ["programmatic"]`:8182- `function` and `custom`83- `mcp`84- `apply_patch`85- Local and hosted `shell`86- `code_interpreter`8788For MCP tools, the tool's `require_approval` policy can pause the program until you approve the call.8990For OpenAI-hosted tools, review the tool's data-retention and security guidance before enabling it in a program.9192### Combine with tool search9394[Tool search](https://developers.openai.com/api/docs/guides/tools-tool-search) runs as a top-level Responses API tool, not from inside generated JavaScript. Function, custom, and MCP tools with `defer_loading: true` are not initially available to a program. After the model loads a matching tool, a later program can invoke it through `tools.*` when its `allowed_callers` includes `"programmatic"`. An already-running program cannot invoke tool search, so the model must load deferred tools before starting a program that needs them.9596## Guide routing when both modes are available9798When your application lets the model call a function directly or from a program, assign each route to a specific workflow stage. Generic instructions such as "use Programmatic Tool Calling efficiently" don't identify the intended boundary. For example:99100```text101<tool_orchestration>102Use Programmatic Tool Calling for [bounded stage] using only [eligible tools].103Run independent calls concurrently when safe. Use only documented tool input104and output fields.105106Process and reduce the intermediate results, then emit exactly [program result shape],107including the evidence needed for the final answer.108109Stop when [condition] is met. Retry transient failures at most [R] times.110Do not repeat completed calls or perform side-effecting actions. If a required111result is still missing, return a clear structured failure.112113Use direct tool calls for [semantic judgment, approval, or final validation].114</tool_orchestration>115```116117Here is an example of how to use this template:118119```text120<tool_orchestration>121Use Programmatic Tool Calling to compare inventory with demand for sku_123122using only get_inventory and get_demand. Run both calls concurrently. Use123only documented tool input and output fields.124125Process and reduce the intermediate results, then emit exactly one JSON object126with sku, available_units, requested_units, and shortage_units, where127shortage_units is max(requested_units - available_units, 0). Include128available_units and requested_units as evidence for the calculation.129130Stop when both tool results contain the required fields. Retry transient131failures at most 1 time. Do not repeat completed calls or perform132side-effecting actions. If a required result is still missing, return a clear133structured failure.134135Use direct tool calls only for approval before any inventory-changing action.136</tool_orchestration>137```138139For workflows that need both modes, define one handoff and avoid switching routes or repeating work. If a safe fallback exists, define it once and limit its retries.140141## Understand program response items142143Each API call still returns the standard [Responses API object](https://developers.openai.com/api/reference/resources/responses/methods/create). Programmatic Tool Calling doesn't introduce a separate response envelope. When the model uses Programmatic Tool Calling, the response's `output` array can contain:144145- A `program` item containing the generated JavaScript, a `call_id`, and an opaque `fingerprint` used to resume or replay the program.146- A `function_call` item made by the program. It has its own `call_id`, which your application uses to return the function result. Its `caller.caller_id` matches the program's `call_id`.147- A `program_output` item containing the program's final result and status. Its `call_id` matches the program's `call_id`, and its `status` is `completed` or `incomplete`.148149These are separate top-level items in `response.output`; the `caller` field records their execution relationship.150151For example, a program can pause while your application runs `get_inventory` and `get_demand`:152153Program and nested function calls154155```json156[157  {158    "type": "program",159    "id": "prog_123",160    "call_id": "call_prog_123",161    "code": "const [stock, demand] = await Promise.all([tools.get_inventory({ sku: 'sku_123' }), tools.get_demand({ sku: 'sku_123' })]); text(JSON.stringify({ sku: stock.sku, available_units: stock.available_units, requested_units: demand.requested_units, shortage_units: Math.max(demand.requested_units - stock.available_units, 0) }));",162    "fingerprint": "opaque_replay_state"163  },164  {165    "type": "function_call",166    "id": "fc_123",167    "call_id": "call_inventory_123",168    "name": "get_inventory",169    "arguments": "{\\"sku\\":\\"sku_123\\"}",170    "caller": {171      "type": "program",172      "caller_id": "call_prog_123"173    }174  },175  {176    "type": "function_call",177    "id": "fc_456",178    "call_id": "call_demand_123",179    "name": "get_demand",180    "arguments": "{\\"sku\\":\\"sku_123\\"}",181    "caller": {182      "type": "program",183      "caller_id": "call_prog_123"184    }185  }186]187```188189190These examples show only the relevant items from `response.output`; they omit the surrounding standard Responses object. After your application returns the nested function results, a later response can contain the complete `program_output` item:191192Program output193194```json195{196  "type": "program_output",197  "id": "prog_out_123",198  "call_id": "call_prog_123",199  "result": "{\\"sku\\":\\"sku_123\\",\\"available_units\\":42,\\"requested_units\\":31,\\"shortage_units\\":0}",200  "status": "completed"201}202```203204205The JSON string in `program_output.result` follows the program result shape from your instructions. The surrounding `program_output` item follows the API contract shown above. These are separate contracts. A final `message` can arrive with the program output or in a later response, so continue until you receive that message.206207OpenAI runs the model-generated JavaScript in the hosted runtime. Your application executes returned client-owned function calls; it does not execute the generated JavaScript.208209Return the function result as a `function_call_output`. Copy `caller` from the function call without changing it. The service uses that value to resume the correct program.210211## Continue after client-owned function calls212213A program can pause more than once as it reaches client-owned tools. Continue until the response contains a final assistant message:2142151. Send the request with the hosted tool and functions that allow programmatic calls.2161. Run every returned client-owned function call.2171. Return each function result with the original `call_id` and `caller`.2181. Handle an incomplete response before continuing.2191. If the response contains no pending `function_call` items and no final `message` item, continue from that response. With `store: false`, replay its output items; for a stored response, use `previous_response_id`.2201. Stop when the response contains a final `message` item. Read `response.output_text` or the message's refusal content.221222The following example uses `store: false`, preserves every response item, and returns each function result to the program:223224Run a programmatic tool-calling loop225226```javascript227import OpenAI from "openai";228import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";229230const client = new OpenAI();231232const implementations = {233  get_inventory: async ({ sku }) => ({ sku, available_units: 42 }),234  get_demand: async ({ sku }) => ({ sku, requested_units: 31 }),235};236237const tools = [238  {239    type: "function",240    name: "get_inventory",241    description:242      "Return an object with sku (string) and available_units (number).",243    parameters: {244      type: "object",245      properties: { sku: { type: "string" } },246      required: ["sku"],247      additionalProperties: false,248    },249    output_schema: {250      type: "object",251      properties: {252        sku: { type: "string" },253        available_units: { type: "number" },254      },255      required: ["sku", "available_units"],256      additionalProperties: false,257    },258    allowed_callers: ["programmatic"],259    strict: true,260  },261  {262    type: "function",263    name: "get_demand",264    description:265      "Return an object with sku (string) and requested_units (number).",266    parameters: {267      type: "object",268      properties: { sku: { type: "string" } },269      required: ["sku"],270      additionalProperties: false,271    },272    output_schema: {273      type: "object",274      properties: {275        sku: { type: "string" },276        requested_units: { type: "number" },277      },278      required: ["sku", "requested_units"],279      additionalProperties: false,280    },281    allowed_callers: ["programmatic"],282    strict: true,283  },284  { type: "programmatic_tool_calling" },285];286287const input = [288  {289    role: "user",290    content: "Compare inventory with demand for sku_123.",291  },292];293294while (true) {295  const response = await client.responses.create({296    model: "YOUR_MODEL_ID",297    store: false,298    input,299    tools,300  });301302  if (response.status !== "completed") {303    throw new Error(`Response ended with status ${response.status}`);304  }305306  // Preserve replayable output, including program and reasoning items.307  input.push(...toResponseInputItems(response.output));308309  const calls = response.output.filter((item) => item.type === "function_call");310311  if (calls.length === 0) {312    const message = response.output.find((item) => item.type === "message");313    if (message) {314      const refusal = message.content.find((part) => part.type === "refusal");315      console.log(response.output_text || refusal?.refusal || "");316      break;317    }318    continue;319  }320321  const outputs = await Promise.all(322    calls.map(async (call) => {323      const run = implementations[call.name];324      if (!run) throw new Error(`Unknown tool: ${call.name}`);325326      const result = await run(JSON.parse(call.arguments));327      return {328        type: "function_call_output",329        call_id: call.call_id,330        output: JSON.stringify(result),331        // Preserve caller so the runtime can resume the correct program.332        caller: call.caller,333      };334    })335  );336337  input.push(...outputs);338}339```340341```python342import json343344from openai import OpenAI345346client = OpenAI()347model = "gpt-6-astra"348349350def get_inventory(sku):351    return {"sku": sku, "available_units": 42}352353354def get_demand(sku):355    return {"sku": sku, "requested_units": 31}356357358implementations = {359    "get_inventory": get_inventory,360    "get_demand": get_demand,361}362363tools = [364    {365        "type": "function",366        "name": "get_inventory",367        "description": "Return an object with sku (string) and available_units (number).",368        "parameters": {369            "type": "object",370            "properties": {"sku": {"type": "string"}},371            "required": ["sku"],372            "additionalProperties": False,373        },374        "output_schema": {375            "type": "object",376            "properties": {377                "sku": {"type": "string"},378                "available_units": {"type": "number"},379            },380            "required": ["sku", "available_units"],381            "additionalProperties": False,382        },383        "allowed_callers": ["programmatic"],384    },385    {386        "type": "function",387        "name": "get_demand",388        "description": "Return an object with sku (string) and requested_units (number).",389        "parameters": {390            "type": "object",391            "properties": {"sku": {"type": "string"}},392            "required": ["sku"],393            "additionalProperties": False,394        },395        "output_schema": {396            "type": "object",397            "properties": {398                "sku": {"type": "string"},399                "requested_units": {"type": "number"},400            },401            "required": ["sku", "requested_units"],402            "additionalProperties": False,403        },404        "allowed_callers": ["programmatic"],405    },406    {"type": "programmatic_tool_calling"},407]408409input_items = [410    {411        "role": "user",412        "content": "Compare inventory with demand for sku_123.",413    }414]415416while True:417    response = client.responses.create(418        model=model,419        store=False,420        input=input_items,421        tools=tools,422    )423424    if response.status != "completed":425        raise RuntimeError(f"Response ended with status {response.status}")426427    # Preserve every output item, including program and reasoning items.428    input_items.extend(item.model_dump(exclude_none=True) for item in response.output)429430    calls = [item for item in response.output if item.type == "function_call"]431    if not calls:432        message = next(433            (item for item in response.output if item.type == "message"), None434        )435        if message:436            refusal = next(437                (part.refusal for part in message.content if part.type == "refusal"),438                "",439            )440            print(response.output_text or refusal)441            break442        continue443444    for call in calls:445        run = implementations.get(call.name)446        if run is None:447            raise ValueError(f"Unknown tool: {call.name}")448449        result = run(**json.loads(call.arguments))450        input_items.append(451            {452                "type": "function_call_output",453                "call_id": call.call_id,454                "output": json.dumps(result),455                # Preserve caller so the runtime can resume the correct program.456                "caller": call.caller.model_dump() if call.caller else None,457            }458        )459```460461```go462package main463464import (465	"context"466	"encoding/json"467	"fmt"468469	"github.com/openai/openai-go/v3"470	"github.com/openai/openai-go/v3/responses"471)472473type toolArguments struct {474	SKU string `json:"sku"`475}476477func main() {478	client := openai.NewClient()479	input := responses.ResponseInputParam{480		responses.ResponseInputItemParamOfMessage(481			"Compare inventory with demand for sku_123.",482			responses.EasyInputMessageRoleUser,483		),484	}485	tools := []responses.ToolUnionParam{486		functionTool(487			"get_inventory",488			"Return an object with sku (string) and available_units (number).",489			"available_units",490		),491		functionTool(492			"get_demand",493			"Return an object with sku (string) and requested_units (number).",494			"requested_units",495		),496		programmaticTool(),497	}498499	for {500		response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{501			Model: "gpt-6-astra",502			Store: openai.Bool(false),503			Input: responses.ResponseNewParamsInputUnion{OfInputItemList: input},504			Tools: tools,505		})506		if err != nil {507			panic(err)508		}509		if response.Status != "completed" {510			panic(fmt.Errorf("response ended with status %s", response.Status))511		}512513		// Preserve every output item, including program and reasoning items.514		input = append(input, outputAsInput(response.Output)...)515516		calls := functionCalls(response.Output)517		if len(calls) == 0 {518			if text, ok := finalMessageText(response); ok {519				fmt.Println(text)520				break521			}522			continue523		}524525		for _, call := range calls {526			result, err := runTool(call.Name, call.Arguments)527			if err != nil {528				panic(err)529			}530			output, err := json.Marshal(result)531			if err != nil {532				panic(err)533			}534535			toolOutput := responses.ResponseInputItemParamOfFunctionCallOutput(string(output))536			toolOutput.OfFunctionCallOutput.CallID = openai.String(call.CallID)537			caller := call.Caller.AsProgram()538			if caller.CallerID == "" {539				panic("function call is missing its program caller")540			}541			// Preserve caller so the runtime can resume the correct program.542			toolOutput.OfFunctionCallOutput.Caller.OfProgram =543				&responses.ResponseInputItemFunctionCallOutputCallerProgramParam{544					CallerID: caller.CallerID,545				}546			input = append(input, toolOutput)547		}548	}549}550551func functionTool(name, description, resultField string) responses.ToolUnionParam {552	parameters := map[string]any{553		"type": "object",554		"properties": map[string]any{555			"sku": map[string]any{"type": "string"},556		},557		"required":             []string{"sku"},558		"additionalProperties": false,559	}560	outputSchema := map[string]any{561		"type": "object",562		"properties": map[string]any{563			"sku":       map[string]any{"type": "string"},564			resultField: map[string]any{"type": "number"},565		},566		"required":             []string{"sku", resultField},567		"additionalProperties": false,568	}569	tool := responses.ToolParamOfFunction(name, parameters, true)570	tool.OfFunction.Description = openai.String(description)571	tool.OfFunction.AllowedCallers = []string{"programmatic"}572	tool.OfFunction.OutputSchema = outputSchema573	return tool574}575576func programmaticTool() responses.ToolUnionParam {577	tool := responses.NewToolProgrammaticToolCallingParam()578	return responses.ToolUnionParam{OfProgrammaticToolCalling: &tool}579}580581func outputAsInput(582	output []responses.ResponseOutputItemUnion,583) []responses.ResponseInputItemUnionParam {584	input := make([]responses.ResponseInputItemUnionParam, 0, len(output))585	for _, item := range output {586		var converted responses.ResponseInputItemUnion587		if err := json.Unmarshal([]byte(item.RawJSON()), &converted); err != nil {588			panic(err)589		}590		input = append(input, converted.ToParam())591	}592	return input593}594595func functionCalls(596	output []responses.ResponseOutputItemUnion,597) []responses.ResponseFunctionToolCall {598	calls := make([]responses.ResponseFunctionToolCall, 0)599	for _, item := range output {600		if item.Type == "function_call" {601			calls = append(calls, item.AsFunctionCall())602		}603	}604	return calls605}606607func finalMessageText(response *responses.Response) (string, bool) {608	for _, item := range response.Output {609		if item.Type != "message" {610			continue611		}612		text := response.OutputText()613		if text != "" {614			return text, true615		}616		for _, content := range item.AsMessage().Content {617			if content.Type == "refusal" {618				return content.AsRefusal().Refusal, true619			}620		}621		return "", true622	}623	return "", false624}625626func runTool(name, argumentsJSON string) (map[string]any, error) {627	var arguments toolArguments628	if err := json.Unmarshal([]byte(argumentsJSON), &arguments); err != nil {629		return nil, fmt.Errorf("parse %s arguments: %w", name, err)630	}631632	switch name {633	case "get_inventory":634		return map[string]any{"sku": arguments.SKU, "available_units": 42}, nil635	case "get_demand":636		return map[string]any{"sku": arguments.SKU, "requested_units": 31}, nil637	default:638		return nil, fmt.Errorf("unknown tool: %s", name)639	}640}641```642643```ruby644require "json"645require "openai"646647client = OpenAI::Client.new648649def get_inventory(sku:)650  {651    sku: sku,652    available_units: 42653  }654end655656def get_demand(sku:)657  {658    sku: sku,659    requested_units: 31660  }661end662663implementations = {664  "get_inventory" => method(:get_inventory),665  "get_demand" => method(:get_demand)666}667tools = [668  {669    type: :function,670    name: "get_inventory",671    description: "Return an object with sku (string) and available_units (number).",672    parameters: {673      type: :object,674      properties: { sku: { type: :string } },675      required: ["sku"],676      additionalProperties: false677    },678    output_schema: {679      type: :object,680      properties: {681        sku: { type: :string },682        available_units: { type: :number }683      },684      required: %w[sku available_units],685      additionalProperties: false686    },687    allowed_callers: [:programmatic],688    strict: true689  },690  {691    type: :function,692    name: "get_demand",693    description: "Return an object with sku (string) and requested_units (number).",694    parameters: {695      type: :object,696      properties: { sku: { type: :string } },697      required: ["sku"],698      additionalProperties: false699    },700    output_schema: {701      type: :object,702      properties: {703        sku: { type: :string },704        requested_units: { type: :number }705      },706      required: %w[sku requested_units],707      additionalProperties: false708    },709    allowed_callers: [:programmatic],710    strict: true711  },712  { type: :programmatic_tool_calling }713]714input = [715  {716    role: :user,717    content: "Compare inventory with demand for sku_123."718  }719]720721loop do722  response = client.responses.create(723    model: "gpt-6-astra",724    store: false,725    input: input,726    tools: tools727  )728  unless response.status == OpenAI::Responses::ResponseStatus::COMPLETED729    raise "Response ended with status #{response.status}"730  end731732  # Preserve every output item, including program and reasoning items.733  input.concat(response.output)734  calls = response.output.grep(OpenAI::Models::Responses::ResponseFunctionToolCall)735736  if calls.empty?737    message = response.output.find do |item|738      item.is_a?(OpenAI::Models::Responses::ResponseOutputMessage)739    end740    next unless message.is_a?(OpenAI::Models::Responses::ResponseOutputMessage)741742    refusal = message.content.find do |content|743      content.is_a?(OpenAI::Models::Responses::ResponseOutputRefusal)744    end745    text = response.output_text746    if text.empty? &&747       refusal.is_a?(OpenAI::Models::Responses::ResponseOutputRefusal)748      text = refusal.refusal749    end750    puts(text)751    break752  end753754  calls.each do |call|755    implementation = implementations.fetch(call.name) do756      raise ArgumentError, "Unknown tool: #{call.name}"757    end758    result = implementation.call(**JSON.parse(call.arguments, symbolize_names: true))759    output = {760      type: :function_call_output,761      call_id: call.call_id,762      output: JSON.generate(result)763    }764    # Preserve caller so the runtime can resume the correct program.765    output[:caller] = call.caller_.to_h if call.caller_766    input << output767  end768end769```770771772When you store responses, you can continue from `previous_response_id` instead of resending all earlier response items. Send the new `function_call_output` items as the next input. With `store: false`, replay the complete sequence in order, including every `program`, reasoning, function-call, function-call-output, and `program_output` item.773774For stateless reasoning-model requests, replay every returned reasoning item. Each item includes `encrypted_content` by default. See [conversation state](https://developers.openai.com/api/docs/guides/conversation-state#manually-manage-conversation-state) for the general stateless pattern.775776## Design tools for programs777778- Return structured, compact data that JavaScript can inspect without parsing prose.779- Use `output_schema` to define each tool's expected return fields and types, and document its error behavior. If the return shape isn't known in advance, keep the tool direct so the model can inspect the result.780- Define the exact program result shape and required evidence. Return a clear structured failure when the program can't produce a valid result.781- Make function calls idempotent when possible. A retry or replay shouldn't repeat an unsafe side effect.782- Check arguments and permissions for each call in your application, even when it comes from a hosted program.783- Give tools specific names and descriptions so the model can compose them correctly.784- Require application-level approval before high-impact actions, regardless of the caller.785786## Evaluate Programmatic Tool Calling787788Programmatic Tool Calling can reduce the amount of intermediate tool output added to model context, but the effect depends on the task and tool responses. Start with direct tool calling as a baseline, then compare both approaches on representative tasks.789790Define the final-answer quality bar and required evidence before measuring efficiency. Evaluate token use and tool calls alongside correctness, completeness, and evidence coverage, and make any accepted quality tradeoff explicit.791792Measure:793794- Final-answer correctness, completeness, and evidence coverage.795- Input and total tokens, end-to-end latency, and cost.796- Model turns, tool calls, retries, and recovery behavior.797- Safety outcomes, especially for side effects and approval requirements.798- Whether the route that ran matched the intended workflow stage.799800## Agents API801802In the [Agents API](https://developers.openai.com/api/docs/guides/agents-api/overview), Programmatic Tool Calling runs in the OpenAI-managed agent harness and is enabled by default. The harness gives the agent an `exec` tool and makes its existing tools available inside generated JavaScript. You don't need to wrap those tools as command-line programs or install them in the sandbox.803804To disable Programmatic Tool Calling, include this entry in `agent.tools`:805806```json807{808  "type": "programmatic_tool_calling",809  "enabled": false810}811```812813Omitting the entry or its `enabled` field leaves Programmatic Tool Calling enabled. A type-only entry, `{ "type": "programmatic_tool_calling" }`, also keeps it enabled. The `allowed_callers` configuration and Responses continuation loop above describe the Responses API integration.814815Programmatic Tool Calling also works in conversation-only sessions with `environment.type` set to `none`. Bash, executor MCPs, and other tools that run in a sandbox still require an [execution environment](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted).816817Orchestrating a tool in JavaScript doesn't change where the tool runs. A shell call runs commands in the sandbox; the JavaScript runtime doesn't start system processes itself. Executor MCPs still use the sandbox, and function tools still call your application server. The agent processes their results before deciding what enters model context.818819Use the routing guidance above to define which workflow stages should use code. Follow [Functions](https://developers.openai.com/api/docs/guides/agents-api/tools/functions) and [MCP connections](https://developers.openai.com/api/docs/guides/agents-api/tools/mcp) for Agents API configuration and call handling.820821## Related guides822823- Use [function calling](https://developers.openai.com/api/docs/guides/function-calling) to define client-owned functions.824- Use [tool search](https://developers.openai.com/api/docs/guides/tools-tool-search) to defer large tool definitions until a model needs them.825- Use [conversation state](https://developers.openai.com/api/docs/guides/conversation-state) to continue stored or stateless Responses API requests.826- Review [data controls](https://developers.openai.com/api/docs/guides/your-data) before choosing a storage mode.
Files main
Menu

Theme