OpenAI Agents API 2026: How Does It Let AI Agents Call Tools? JSON Schema, Tool Calling, and Workflow Data
On 10 September 2026 OpenAI shipped the Codex harness as the Agents API public beta. The more accurate engineering sentence is: the model only fills tool JSON; the managed harness runs the loop, compacting context and dispatching subagents. This piece opens that chain: how Schema gates a call, how Tool Calling proposes one, and where the workflow objects land.
At a glance
| Idea | One line | Lands on |
|---|---|---|
| Agents API | OpenAI-hosted Codex harness | sessions.create / agent |
| Tool Calling | The model proposes a name and arguments | function_call / arguments |
| JSON Schema | The argument shape allowed into the loop | parameters / additionalProperties |
| Workflow | Session, turn, items, handoff | session / turn / items |
Remember the split: you declare Agent, Tools, Environment; OpenAI runs the loop. Side effects still land in the host or sandbox, not in the weights.
It can call tools. The model is not the caller
After the public beta, one POST /v1/agents/sessions can start a long-running agent: look up an order, read a repo, dispatch a subagent. Those side effects are real.
“Call tools directly” puts the subject in the wrong place. The model does not hold the MCP socket, does not spawn the sandbox, and does not hit your billing API. It emits a validatable object. What runs is the managed harness and the Environment you chose.
- Declare: put functions, MCP, and web_search on
agent.tools. - Propose: the model emits a
function_call— a name plus an argument object. - Execute: the harness hits a service tool, your function handler, or a command in the sandbox.
If Agent, Tool Calling, and Function Calling still collapse into one word, start with AI Agent complete guide. This article covers one fact: how Agents API hosts that JSON chain.
What the 10 September beta actually shipped
OpenAI said it plainly: the same harness behind Codex and ChatGPT for Work, as an API. On the model side you often see GPT-6 Astra guide. You pay tokens and tool usage; the API itself adds no extra fee.
- Managed loop: session, orchestration, recovery — without your own LangChain glue.
- Context compaction: auto-compact near the window limit so work can span windows.
- Tool search + programmatic tool calling: load definitions on demand, run calls in parallel, write only relevant results back.
- MCP / function / web_search: one tool table can mix them.
- Subagents:
multi_agent.enabled, with a concurrency cap (docs often default to 6).
OpenAI DevDay 2026 predictions guessed at Structured Outputs and Function Calling. What landed is a session resource: Agent, Environment, Session, Events / Items — not another chat-completion wrapper.
JSON Tool Calling is still the skeleton
The API name changed. The four hops did not:
- Declare:
agent.tools[]offers a function Schema, an MCP server, built-in tools. - Propose: the model fills a name and arguments. It does not execute.
- Gate: the harness or your handler validates, allowlists, and may wait for a human.
- Write back: a tool result becomes the next visible item.
A session create looks roughly like this. The order is fictional — do not paste real secrets or intranet URLs onto a public site:
{
"agent": {
"model": "gpt-6-astra",
"instructions": "Look up orders. Confirm before any refund.",
"tools": [
{ "type": "programmatic_tool_calling" },
{
"type": "function",
"name": "lookup_order",
"parameters": {
"type": "object",
"properties": {
"order_id": { "type": "string", "pattern": "^[A-Z]-\\d{4}$" }
},
"required": ["order_id"],
"additionalProperties": false
}
},
{
"type": "mcp",
"server_label": "orders",
"transport": {
"type": "http",
"server_url": "https://orders.example.com/mcp"
}
}
],
"multi_agent": {
"enabled": true,
"max_concurrent_subagents": 3
}
},
"environment": {
"type": "hosted",
"workspace_directory": "/workspace"
},
"input": [
{
"role": "user",
"content": [
{ "type": "input_text", "text": "Look up order A-1042." }
]
}
]
}
Once the loop turns, the model still emits an object, not a paragraph that says “please look up the order”:
{
"type": "function_call",
"id": "call_3c91",
"name": "lookup_order",
"arguments": "{\"order_id\":\"A-1042\"}"
}
The MCP row is another spelling of the same tool table; protocol boundary in MCP Tool Schema vs Function Calling. A coding agent running commands in the sandbox is still JSON — see AI Coding Agents and the JSON call chain.
JSON Schema is the gate, not “please return JSON”
Hosting the loop does not make a wide object safe. Contract in Why AI needs JSON Schema: the prompt influences what it fills; Schema decides what can execute.
{
"name": "lookup_order",
"description": "Look up an order by ID",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"pattern": "^[A-Z]-\\d{4}$"
}
},
"required": ["order_id"],
"additionalProperties": false
}
}
A function Schema that can actually block a bad hop needs four things:
- Tighten types: give
order_ida pattern, not a barestring. - Enum amounts and actions: refunds and status changes need an
enum. - Close extra keys:
additionalProperties: false. A wide object is another door. - The host writes confirmation: the model may propose
create_refund; it must not writeallowed: trueinto state.
Keep tools and workflows as Schema in the repo, not as a chat agreement. How to string the gates: JSON Schema and AI Agent workflows.
The workflow is four objects, not a prompt chain
OpenAI folds Agents API into four concepts. In engineering they are all JSON:
- Agent: model, instructions, tools, MCP. The capability table.
- Environment: a hosted or self-hosted sandbox. Files, commands, and skills live here.
- Session: a durable instance. State stays on the service; you do not rebuild history.
- Events / Items: input, tool calls, results, subagent coordination.
An auditable workflow should look like objects, not a last sentence that says “done”:
{
"session_id": "sess_9f2a",
"turn": 2,
"items": [
{ "type": "function_call", "name": "lookup_order", "id": "call_3c91" },
{ "type": "function_call_result", "call_id": "call_3c91", "status": "paid" }
],
"state": {
"order_id": "A-1042",
"status": "paid"
},
"next": {
"tool": "create_refund",
"allowed": false,
"reason": "missing_confirmation"
}
}
Each subagent keeps its own context. The main agent coordinates with create / message / wait / interrupt items. Cap concurrency. Without a cap, tokens and side effects fan out.
Agents API vs Responses API vs SDK: who runs the loop
The question is not who is smarter. It is where orchestration lives:
| Axis | Agents API | Agents SDK | Responses API |
|---|---|---|---|
| Who runs the loop | OpenAI-hosted Codex harness | A runner in your process | You assemble it, or optional hosted orchestration |
| Where state lives | Session / turns / items | Your store or an SDK session | History, response chains, Conversations |
| Who executes tools | Service tools, handlers, sandbox | Tools configured in the app | Hosted tools or your own runtime |
| Use when | Hour-long jobs that need hosted recovery | You must own approvals and deploy | You call the model, or build an agent from scratch |
Pick the subject before you pick the API: do you want to own the loop? The full comparison is the next piece. This one only asks you to see: swap the entry point, the JSON call chain stays.
Inspect the objects, not “the agent finished”
After a session ends, do not review the last natural-language sentence. Review the trail.
- format
agent.tools,function_call, andfunction_call_result. Check that the name and arguments are the hop you thought. - Use JSONPath extract to pull
name,arguments, andserver_url. Do not scan prose by eye. - JSON Diff against a session trail that passed last week. If the loop drifted, look at the tool table and argument shape before you blame the model.
- Compaction summaries must still carry the key fields. Lose
order_idand the next hop fills the wrong one. - Do not paste orders, sessions, or intranet MCP URLs into a public formatter. JSONSort runs in the browser locally; that is the right place to read these objects.
If the trail can be rebuilt, the session can be reviewed. That is the entry ticket into production, not a nice-to-have.
Pitfalls
- Thinking Agents API lets the model execute tools. Execution lives on the harness and the Environment.
- Handing a wide
objectto a managed loop. Hosted is not a gate. - Enabling multi_agent without a concurrency cap. Subagents fan out tokens and side effects.
- Treating a Session as another chat completion. State, recovery, and compact are first-class objects.
- Replacing Schema with a prompt. The contract must be validatable.
- Reading a hosted sandbox as Zero Data Retention. OpenAI is explicit: a self-hosted sandbox does not make Agents API ZDR-eligible.
FAQ
Does Agents API let the model call tools directly?
It lets the model emit tool JSON directly. Execution still sits in the managed harness, your function handler, or the sandbox. No tools[], it calls nothing.
Agents API or Responses API — where do I start?
Want OpenAI to run a long loop, compact context, and dispatch subagents? Agents API. Want to own every model call? Responses API. Want the loop in your process? Agents SDK.
Do I still need JSON Schema on Agents API?
Yes. The managed loop only orchestrates. Function parameters, MCP inputSchema, and state objects still need to be tight. A wide Schema plus an automatic loop opens a wider door.
What is the difference between Session, Turn, and Item?
A Session is the durable instance. A Turn is work started by one user input. Items are the inputs, tool calls, results, or subagent coordination inside that stretch. Audit items, not the last sentence.
Should an agent touch a production database or issue a production refund?
Do not put production side effects on the first hop. Let it draft in an isolated environment behind a tight Schema. The host writes confirmation. Secrets and release stay a human gate.
Conclusion
Agents API lets an agent “call tools directly” — that sentence is only half true. The true half: you no longer write the loop. The false half is the subject. The subject is the harness; the verb is execute; the object is JSON the model filled.
From today, argue less about whether it “does the work itself.” Put Agent, Tools, Session items, and Schema in the repo first. Models will change. This JSON tool-calling chain should stabilize first.
Further reading
- What is an AI Agent? Complete guide Runtime, tool calling, and the JSON contract
- JSON Schema and AI Agent workflows Capture tools and state as Schema
- MCP Tool Schema vs Function Calling Protocol boundary for tool JSON
- Why AI needs JSON Schema Schema / Function Calling / Structured Output
- OpenAI DevDay 2026 predictions The older Structured Outputs / Agent framing
- JSONSort local toolbox Format, validate, Diff
- All posts More JSON and AI development notes
Changelog: First published