← All articles

GPT-5.6 Programmatic Tool Calling: Migration Guide for Routing and Agent Workflows

GPT-5.6 introduces Programmatic Tool Calling — the model writes JavaScript to orchestrate your tools inside a V8 sandbox, cutting token use by 24–63% and eliminating round-trip overhead. The catch: it only works on the Responses API. We walk through the migration from classic function calling, highlight every breaking change, and show how to route GPT-5.6 through an OpenAI-compatible gateway during the transition.

· TheRouter

GPT-5.6 shipped on July 9, 2026. Its headline developer feature isn't a new benchmark score — it's Programmatic Tool Calling, a mechanism that lets the model write and run JavaScript to coordinate your tools inside a sandboxed V8 runtime. Instead of bouncing every tool result back through the model for re-reasoning, GPT-5.6 can loop, branch, filter intermediate data, and return only the final result. OpenAI reports 24% fewer output tokens on average, with early customers seeing up to 63.5% reduction on tool-heavy workflows.

The requirement: Programmatic Tool Calling is Responses API only. If your agents still call /v1/chat/completions, you get none of this. This guide walks you through the migration — what changes, what breaks, and how to route traffic safely during the transition.

  1. Swap three values, not three SDKs. Change api_key, base_url, and model in the existing OpenAI client. Keep your request/response code unchanged.
  2. Map model IDs explicitly. The target provider's model id is almost never identical to the OpenAI id. Keep a single dict of { openai_id: target_id } outside business logic.
  3. Verify streaming format. SSE chunks must follow the OpenAI data: {...} + data: [DONE] contract. Test one streaming call before moving production traffic.
  4. Check rate-limit headers. Some providers omit x-ratelimit-* headers. Add a wrapper that defaults safely when headers are absent.
  5. Keep a rollback path. Ship the swap behind a feature flag, run both endpoints in shadow for 24 hours, then cut over.

Sources: OpenAI GPT-5.6 Announcement, retrieved 2026-07-19; Programmatic Tool Calling Documentation, retrieved 2026-07-19; ByteIota Migration Analysis, retrieved 2026-07-19; OpenAI API Pricing, retrieved 2026-07-19.

TL;DR — Before/After

DimensionClassic Function Calling (/v1/chat/completions)Programmatic Tool Calling (Responses API)
OrchestrationDeveloper scripts every step, or model issues one tool call per turnModel writes JavaScript to coordinate tools
Round tripsOne API call per tool → model → next toolOne program handles N tools → single result
Token costEvery intermediate result enters contextOnly program_output enters context
Parallel callsDeveloper-managed or tool_choice: "auto"Native Promise.all() inside the V8 sandbox
Supported modelsAll GPT-4/5.x modelsGPT-5.6 Sol, Terra, Luna only
API endpoint/v1/chat/completions/v1/responses

What Is Programmatic Tool Calling?

When you enable Programmatic Tool Calling, GPT-5.6 can emit a program output item containing JavaScript. OpenAI runs that code in a fresh, isolated V8 runtime. The program can:

  • Call your tools via await tools.your_function_name({ ... })
  • Run calls in parallel with Promise.all()
  • Use loops, conditionals, and intermediate variables
  • Filter or aggregate tool results before returning

Your application still executes the actual tool functions — the V8 sandbox has no network access, no Node.js APIs, no persistent state. It orchestrates; you execute.

The result: the model sees a single program_output item instead of 10 separate function_call_output items. For a 10-step tool workflow, that's roughly 10× less context accumulation.

The Breaking Changes

Migrating from /v1/chat/completions to the Responses API introduces several structural changes. Here's what to audit:

1. Response structure changes completely

Before (Chat Completions):

{
  "choices": [{
    "message": {
      "role": "assistant",
      "tool_calls": [{
        "id": "call_abc123",
        "function": { "name": "get_weather", "arguments": "{\"city\":\"Shanghai\"}" }
      }]
    }
  }]
}

After (Responses API):

{
  "output": [
    {
      "type": "program",
      "call_id": "prog_abc123",
      "code": "const weather = await tools.get_weather({city: 'Shanghai'});\ntext(JSON.stringify(weather));",
      "fingerprint": "fp_xyz..."
    },
    {
      "type": "function_call",
      "call_id": "fc_001",
      "name": "get_weather",
      "arguments": "{\"city\":\"Shanghai\"}",
      "caller": { "caller_id": "prog_abc123" }
    }
  ]
}

The choices[0].message wrapper is gone. You get a typed output array with distinct item types (program, function_call, function_call_output, program_output, message).

2. Tools declaration gains allowed_callers

Before:

tools = [
    {"type": "function", "function": {"name": "query_db", "parameters": {...}}}
]

After:

tools = [
    {"type": "programmatic_tool_calling"},  # Enable the feature
    {
        "type": "function",
        "name": "query_db",
        "parameters": {...},
        "output_schema": {...},  # Helps the JS use typed results
        "allowed_callers": ["programmatic"]  # Or ["direct", "programmatic"]
    }
]

Three key additions:

  • Add {"type": "programmatic_tool_calling"} to the tools list
  • Set allowed_callers on each function ("direct", "programmatic", or both)
  • Add output_schema so the generated JavaScript can reliably access return fields

3. Parallel dispatch is now default

In the Responses API, GPT-5.6 will issue parallel tool calls by default. If your tool handlers assume serial execution (e.g., tool B depends on tool A's side effect), they will silently break. Audit your tool dependency graph.

4. Responses are stored server-side by default

Unlike Chat Completions (stateless), the Responses API stores responses server-side. For privacy-sensitive workflows, set store: false explicitly. Note: store: false enables stateless continuation but does not enable Zero Data Retention (ZDR) by itself — ZDR must be enabled at the organization or project level.

5. code_interpreter → tools.runtime namespace

The old code_interpreter tool type is replaced by a tools.runtime namespace with per-call billing. Budget accordingly if you rely on code execution.

Step-by-Step Migration Checklist

Step 1: Audit your current tool-calling patterns

# Find all chat/completions calls with tools in your codebase
grep -rn "chat.completions\|/v1/chat/completions" --include="*.py" --include="*.ts" .
grep -rn "tool_calls\|function_call" --include="*.py" --include="*.ts" .

Classify each call site:

  • Single tool call → single result: Keep as direct tool calling on Responses API
  • Multi-step tool chains: Candidate for Programmatic Tool Calling
  • Semantic judgment between steps: Keep as direct tool calling (the model needs to reason between each result)

Step 2: Migrate to the Responses API (without Programmatic Tool Calling first)

Swap your endpoint and response parsing:

# Before
response = client.chat.completions.create(
    model="gpt-5.5",
    messages=[{"role": "user", "content": "..."}],
    tools=[{"type": "function", "function": {"name": "get_weather", ...}}]
)
tool_call = response.choices[0].message.tool_calls[0]

# After — Responses API, classic direct calling
response = client.responses.create(
    model="gpt-5.6-terra",
    input=[{"role": "user", "content": "..."}],
    tools=[{"type": "function", "name": "get_weather", "parameters": {...}}]
)
for item in response.output:
    if item.type == "function_call":
        result = execute_tool(item.name, item.arguments)

Step 3: Enable Programmatic Tool Calling for eligible workflows

For tool-heavy, bounded workflows:

response = client.responses.create(
    model="gpt-5.6-terra",
    input=[{"role": "user", "content": "Compare inventory across all 5 warehouses"}],
    tools=[
        {"type": "programmatic_tool_calling"},
        {
            "type": "function",
            "name": "get_inventory",
            "parameters": {"type": "object", "properties": {"warehouse_id": {"type": "string"}}},
            "output_schema": {
                "type": "object",
                "properties": {"warehouse_id": {"type": "string"}, "units": {"type": "number"}},
                "required": ["warehouse_id", "units"]
            },
            "allowed_callers": ["programmatic"]
        }
    ]
)

# Handle the program's tool calls
for item in response.output:
    if item.type == "function_call":
        result = execute_tool(item.name, item.arguments)
        # Return result linked back to the program via caller.caller_id

Step 4: Add system prompt guidance

GPT-5.6 responds well to explicit orchestration instructions:

Use Programmatic Tool Calling for the data-gathering stage using only
get_inventory and get_metrics. Run independent calls concurrently.

Process intermediate results and return a single JSON summary.
Retry transient failures at most 2 times.

Use direct tool calls for any write operation requiring approval.

Rollback Path

If Programmatic Tool Calling produces unexpected results:

  1. Remove {"type": "programmatic_tool_calling"} from tools — the model falls back to direct tool calling on the same Responses API endpoint
  2. Switch model to gpt-5.5 — still works on the Responses API with direct tool calling
  3. Revert to Chat Completions entirely — change endpoint back to /v1/chat/completions and use any GPT-5.x model (Programmatic Tool Calling won't activate)

The Responses API supports both modes simultaneously. You can enable Programmatic Tool Calling for some tools (allowed_callers: ["programmatic"]) and keep others as direct-only (allowed_callers: ["direct"]).

Cost Impact

ModelInput / 1M tokensOutput / 1M tokensNotes
gpt-5.5$5.00$25.00Your current baseline
gpt-5.6-sol$5.00$30.00Frontier; +20% output cost vs 5.5
gpt-5.6-terra$2.50$15.00Recommended; –50% input, –40% output vs 5.5
gpt-5.6-luna$1.00$6.00High-volume; –80% input, –76% output vs 5.5

Prices from OpenAI, retrieved July 19, 2026.

With Programmatic Tool Calling on Terra, the combined effect is dramatic:

  • Model cost: –50% input / –40% output (Terra vs GPT-5.5)
  • Token reduction: –24% to –63.5% (fewer round trips, less context accumulation)
  • Net savings: Typically 60–80% cheaper per completed workflow vs GPT-5.5 with classic tool calling

When NOT to Use Programmatic Tool Calling

Keep direct tool calling when:

  • Each result changes the plan — semantic judgment between steps requires the model to reason
  • Write operations need approval — keep authorization boundaries clear
  • Tools have side effects that depend on order — the V8 sandbox may parallelize calls
  • You need full citation provenance — program_output collapses intermediate steps
  • The workflow is single-step — overhead of program generation isn't worth it for one call

V8 Sandbox Constraints

The V8 runtime has hard limits:

  • No network access (tools are the only external interface)
  • No Node.js APIs, no require(), no import of packages
  • No persistent state between executions
  • No console.log (use text() or image() to emit output)
  • No subprocess execution or filesystem access
  • Only tools with allowed_callers: ["programmatic"] are callable

These are security features. The program can only interact with external systems through your explicitly opted-in tools.

Routing Through an OpenAI-Compatible Gateway

If you route API traffic through TheRouter or another OpenAI-compatible gateway, the migration path is the same — just point your base_url at your gateway and use gpt-5.6-terra as the model ID. The Responses API format passes through transparently when your gateway supports it.

For teams mid-migration, a common pattern:

  1. Route gpt-5.6-terra traffic through your gateway for new Programmatic Tool Calling workflows
  2. Keep gpt-5.5 on a fallback route for existing Chat Completions code
  3. Migrate call sites incrementally; monitor token usage per route

This avoids a big-bang cutover and lets you compare costs side-by-side.

Timeline and Recommendations

TimeframeAction
NowAudit tool-calling patterns; classify as programmatic-eligible or direct-only
Week 1–2Migrate to Responses API with direct tool calling (no Programmatic Tool Calling yet)
Week 2–3Enable Programmatic Tool Calling on bounded, tool-heavy workflows
Week 3–4Benchmark token savings; adjust model tier (Sol → Terra → Luna) based on quality needs
Q1 2027response_format: {"type": "json_object"} loses guaranteed-shape behavior — migrate to JSON Schema 2025-12

FAQ

Q: Do I have to use Programmatic Tool Calling to use GPT-5.6? No. GPT-5.6 works with standard direct tool calling on the Responses API. Programmatic Tool Calling is opt-in via the programmatic_tool_calling tool type.

Q: Can I use Programmatic Tool Calling with the Chat Completions API? No. It requires the Responses API (/v1/responses).

Q: What about streaming? The Responses API supports streaming. Program execution events stream as they occur — you'll see program, function_call, and program_output items appear incrementally.

Q: Is GPT-5.6 in TheRouter's models-data.ts? Not yet at time of writing. GPT-5.6 model IDs (gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna) are confirmed via OpenAI's API but may not be reflected in all routing configurations immediately. Check your gateway's live /models/ endpoint for current availability.


GPT-5.6 launched July 9, 2026. Programmatic Tool Calling documentation last updated at the time of this writing (July 19, 2026). Pricing, model availability, and sandbox constraints may change — verify against OpenAI's official docs before production deployment.

Internal Links

Models covered in this article

Help & contact