AI Development

How to Migrate JSON Tool Calling After the OpenAI DevDay 2026 API Update

About 16 min read

Change the write-back before you change the model id. If the target is gpt-6-astra or gpt-6.1-sol, tool calling leaves Chat Completions and uses the Responses API. A hop that only classifies or routes can move to the Decisions API, public beta since 6 October. The lookup stays lookup_order. The model only fills JSON. The host runs it.

Before you read

Object Official fact What changes in the migration
Endpoint Astra and GPT-6.1 Sol tool calls use Responses. Sol and Luna can call functions on Chat Completions only when reasoning_effort is none Move to POST /v1/responses, or keep none on purpose
Tool object On Responses, name sits beside type. Omitting strict tries strict mode, then falls back and marks strict: false Set strict explicitly. The host still validates
Write-back function_call and function_call_output share a call_id. async: true does not make the model run the tool Replace role:tool with function_call_output
Routing Decisions went public beta on 2026-10-06. Only gpt-6-luna. A choice does not run a tool Move only the hop that picks an enum

Facts from the official docs as of 2026-10-08: Using GPT-6, Migrate to the Responses API, Decisions, Async tool calling. Order A-1042 is fictional. Probabilities in the Decisions sample are illustrative.

Change the write-back shape, then the model id

After DevDay, the migration is a field change. It is not another essay on what an agent is. The official notes come down to four lines. First, if the target model is gpt-6-astra or gpt-6.1-sol, tool calling moves to Responses. Those two can still send Chat Completions requests that do not use tools. gpt-6-sol and gpt-6-luna can call functions on Chat Completions only when reasoning_effort is none. Reasoning plus tools uses Responses. Second, the tool name moves out of a nested function object and sits beside type. Third, the write-back id moves from tool_calls[].id to call_id. Fourth, a hop that only classifies or routes can move to Decisions. Decisions does not run a tool.

  • Astra / GPT-6.1 Sol: tool calling leaves /v1/chat/completions. On Responses the effort field is reasoning.effort.
  • Flatten the definition and set strict. Chat Completions is non-strict by default. Responses tries strict mode when strict is omitted.
  • Split routing from the lookup. A choice returns an enum. The lookup stays lookup_order.

What shipped on 29 September: what DevDay 2026 shipped. How Responses and the Agents API differ as entry points: how the Agents API and Responses differ. This piece does not rewrite those two. It only maps the fields in an existing JSON tool-calling loop.

Name the call your code reads today

On a Chat Completions reply, one call looks like this. The id is outside. The name is inside function. arguments is a string. Fictional order — no real secrets:

{
  "id": "call_3c91",
  "type": "function",
  "function": {
    "name": "lookup_order",
    "arguments": "{\"order_id\":\"A-1042\"}"
  }
}

A Responses tool puts name beside type. Do not omit strict. If you do, Responses tries strict mode. An incompatible schema falls back, and the returned tool shows strict: false. To keep non-strict behavior, set false. The host still validates arguments before it runs:

{
  "type": "function",
  "name": "lookup_order",
  "strict": true,
  "parameters": {
    "type": "object",
    "properties": {
      "order_id": { "type": "string", "pattern": "^[A-Z]-\\d{4}$" }
    },
    "required": ["order_id"],
    "additionalProperties": false
  }
}

The write-back keys off call_id

The model emits a function_call item, not a tool_calls array. Point the parser at these four fields:

{
  "type": "function_call",
  "call_id": "call_3c91",
  "name": "lookup_order",
  "arguments": "{\"order_id\":\"A-1042\"}"
}

The host parses arguments, checks Schema, runs lookup_order, and writes back on the same call_id. A failure is an object inside the string too. If you replay history by hand, send the previous reasoning items and the function_call with the result. Do not send the result alone. previous_response_id is the shorter path:

{
  "type": "function_call_output",
  "call_id": "call_3c91",
  "output": "{\"order_id\":\"A-1042\",\"status\":\"paid\"}"
}

A slow tool can set async: true on the definition. The docs: the model may keep reasoning, or answer parts that do not depend on that result; the call item echoes async: true; you later submit function_call_output with the original call_id. The host still runs the tool. Built-in tools and programmatic tool calling cannot use async. In multi-agent mode, do not combine async tools with parallel tool calls. The docs name GPT-6 Astra and later models.

Five loops. Do not merge them into one upgrade

Migrate by the write-back you already have. Different object names need different parsers:

Loop you have Where it goes
Chat Completions + tools, target Astra or GPT-6.1 Sol Move to POST /v1/responses. Map tool_calls[].id to call_id. Lift name out of function.
Chat Completions + tools, staying on gpt-6-sol or gpt-6-luna You can stay only while reasoning_effort is none. Reasoning plus tools moves to Responses.
Already a Responses function_call Keep the loop. Swap the model id. Astra and GPT-6.1 Sol do not support none; use low. When effort is not none, drop temperature, top_p, and top_logprobs.
A tool that only returns lookup / hold / escalate Move that hop to POST /v1/decisions on gpt-6-luna. The lookup stays a function_call.
An Agents API session Stay on Agents if you want the managed harness. The write-back stays turn_id, not function_call_output.

The routing hop uses a Decisions choice. The request follows the 6 October public-beta shape. The order is fictional. Do not POST the homemade answer contract from the 30 September piece to /v1/decisions:

{
  "model": "gpt-6-luna",
  "input": "Order A-1042 is paid. What should the host do next?",
  "questions": [{
    "type": "choice",
    "name": "next_step",
    "instructions": "Pick the next host action. Do not invent a tool name.",
    "choices": [
      { "value": "lookup", "description": "Read the order. No refund." },
      { "value": "hold", "description": "Stop and wait." },
      { "value": "escalate", "description": "Hand the case to a person." }
    ]
  }]
}

The sample reply uses the official choice field names. The probabilities are fictional. The host calls lookup_order only after choice is lookup. hold stops. escalate goes to a person. A refusal does not call the tool. You pick the threshold. Images must be inline base64 data URLs. HTTPS image URLs and file_id are not accepted on this endpoint:

{
  "answers": [{
    "type": "choice",
    "name": "next_step",
    "choice": "lookup",
    "probabilities": [
      { "value": "lookup", "probability": 0.91 },
      { "value": "hold", "probability": 0.05 },
      { "value": "escalate", "probability": 0.04 }
    ],
    "confidence": 0.88
  }]
}

Ultrafast is service_tier set to ultrafast on gpt-6-astra. It supports US data residency and global processing only, not EU or other regional endpoints, and it does not change tool JSON. GPT-6.1 Sol Ultrafast was still described as coming in the 29 September recap. This piece does not call it shipped. On 29 September the Agents API added Computer Use in an OpenAI-hosted browser. Your application still handles site approvals and sign-in. That is not a reason to replace Responses function_call_output. Misalignment monitoring can alert or stop a conversation. When a turn stops, do not blindly replay the same tool call.

Six steps, then flip traffic

Do not start from turning on Ultrafast. Write the write-back:

  1. Name the current write-back. It is role: tool plus tool_call_id, or function_call_output, or an Agents turn_id. Do not share one parser across the three.
  2. Pick the endpoint. Astra and GPT-6.1 Sol tool calls move to POST /v1/responses. Sol and Luna can stay on Chat Completions only with reasoning_effort set to none.
  3. Flatten the tool and set strict. If the schema uses a form strict mode rejects, set strict: false and keep the host check. A lookup accepts only order_id.
  4. Align call_id and drop sampling params. arguments stays a string. On Astra and GPT-6.1 Sol, replace none with low. When effort is not none, remove temperature, top_p, and top_logprobs. On Chat Completions also remove logprobs. On Responses, remove message.output_text.logprobs from include. Coming from GPT-5.5 or earlier, replace prompt_cache_retention with prompt_cache_options.ttl set to 30m. To change effort mid-conversation, send a configuration_update item and leave request-level reasoning.effort unchanged.
  5. Move only the enum hop to Decisions. The model is gpt-6-luna. The host maps lookup to lookup_order. Add async: true only for a slow tool, and write back on the original call_id.
  6. Diff the two JSON payloads, then flip traffic. Schema-check arguments and answers[].choice. If they do not match, do not swap the model id.

How Schema is a gate: Agents API + JSON Schema. Why that gate cannot live only in the prompt: why AI needs JSON Schema. The Agents write-back shape: Agents API tool JSON.

Before you flip traffic, spread three JSON objects

Most failures are a mismatched id, a name still nested under function, or a choice treated as an order that was already looked up.

  • format the old tool call beside the new function_call, then Schema validate whether arguments and answers[0].choice pass the gate.
  • Use JSONPath extract for $.call_id, $.name, and $.answers[0].choice.
  • JSON Diff the payload before and after the move. Fix call_id before you rewrite the prompt.
  • Check that call_3c91 is the same id on the call and on function_call_output.
  • Do not paste orders, tokens, or repo paths into a public formatter. JSONSort runs in the browser.

If those three objects rebuild against Schema, the migration is done. Ultrafast does not replace that check.

Common traps

  • Treating Decisions as the new tool-calling API. It returns a choice, a probability, or a score. Tool arguments still use Responses function calling. An object that follows your own schema uses Structured Outputs on Responses.
  • Leaving the nested function object. Responses does not read the old function.name. The name sits beside type.
  • Omitting strict and expecting non-strict. Responses tries strict mode, then falls back and marks strict: false when the schema cannot comply. Write the behavior you want. Keep the host check.
  • Leaving none or temperature on Astra or GPT-6.1 Sol. Those models do not support none. GPT-6.1 Sol also does not support minimal. Use low. When effort is not none, drop the sampling params.
  • Moving a Responses loop into the Agents API for Computer Use. The 29 September add-on is a hosted browser. Approvals stay in your app. The Agents write-back is turn_id.
  • Reading async: true as the model running the tool. The host still runs it and tracks pending work. Write back later on the original call_id.

FAQ

Do I have to leave Chat Completions?

If the request uses tools and the model is gpt-6-astra or gpt-6.1-sol, move those calls to Responses. Chat Completions can still carry requests without tools on those models. gpt-6-sol and gpt-6-luna can call functions on Chat Completions only with reasoning_effort set to none. Reasoning plus tools uses Responses.

Can Decisions emit a function_call?

No. It returns a typed answer: a predicate probability, a choice, or a score. The host maps the choice onto a function you already have. The public-beta endpoint is POST /v1/decisions, and the only model today is gpt-6-luna. OpenAI describes it as about 10× faster than Responses. That is classification speed, not an order lookup.

What happens if I omit strict?

Chat Completions is non-strict by default. On Responses, omitting strict tries strict mode. If the schema cannot be made compatible, the API falls back and the returned tool shows strict: false. Set false explicitly to keep non-strict behavior. The host still validates arguments either way.

Should I change the write-back on an Agents session?

No. The Agents API is still in public beta. Keep turn_id. Computer Use is a separate permission on a hosted browser. Your app handles site approvals and sign-in. If you want to own every hop, stay on Responses and function_call_output.

Does Ultrafast change the tool JSON?

No. It is service_tier: ultrafast on gpt-6-astra, with US data residency and global processing only. The tool definition, call_id, and Schema stay. Do not write GPT-6.1 Sol Ultrafast as already available.

Conclusion

Migrating JSON tool calling after DevDay 2026 means changing the write-back. Astra and GPT-6.1 Sol tool calls use Responses. The name sits beside type. Results return on call_id. The hop that only picks an enum can move to Decisions. The host still runs the lookup.

Write down whether A-1042 goes into lookup_order arguments or into a Decisions input. Point at call_3c91 and answers[0].choice before you flip traffic.

Further reading

Changelog: First published