Human in the Loop with the OpenAI Agents SDK
Human approval with the OpenAI Agents SDK, explained for businesses and their IT providers: approval rules, saved runs and someone responsible for checking.
· 5 min read · by the Yoonet team · Updated

Educational example: the reviewer endpoint below is a non-working placeholder for a service your provider would need to implement. It is not a hitl.ph API. Use our setup kit to prepare a workflow and rehearse locally.
If your IT provider uses the OpenAI Agents SDK, they can build a human approval step into an automated process. Your business decides the rule: for example, a refund above an agreed amount needs a manager’s approval.
This guide explains the terms your provider may use—needs_approval, interruptions and RunState—alongside the staffing decisions you need to make. The Python examples are integration sketches that need adaptation and testing against the SDK version and API contract in use.
Step 1 — Define which actions need a check
The example uses needs_approval to identify refund requests above a sample amount. That amount is an example, not a recommended policy. Your business should agree its own limits and who has authority to approve them.
from agents import Agent, Runner, function_tool
async def refunds_over_100(_ctx, params, _call_id) -> bool:
# Approval is conditional: only pause when the stakes are real.
return params.get("amount", 0) > 100
@function_tool(needs_approval=refunds_over_100)
async def issue_refund(order_id: str, amount: float) -> str:
do_refund(order_id, amount) # your implementation
return f"Refunded {amount} on {order_id}"
agent = Agent(
name="Support agent",
instructions="Resolve support cases. Use tools when needed.",
tools=[issue_refund],
)The reviewer needs enough context to make the decision: the order, the reason for the refund and the applicable rule.
Step 2 — Collect the pending decisions
An interruption is a request waiting for approval. This example takes the pending requests, records approve or reject decisions and continues the run. One process may create more than one approval request.
result = await Runner.run(agent, "Customer 1042's order arrived broken, refund $180")
while result.interruptions:
state = result.to_state()
for interruption in result.interruptions:
if ask_someone(interruption.name, interruption.arguments): # the hard part
state.approve(interruption)
else:
state.reject(interruption)
result = await Runner.run(agent, state)
print(result.final_output)For a business workflow, ask_someone() needs to become a real review process. Decide who sees the request and how it is assigned, rather than assuming someone will notice an alert.
Step 3 — Keep a record while the person responds
RunState represents the saved state of the process. The example stores it and reloads it later, so a response does not have to happen in the same immediate session. LangGraph uses checkpointers for a similar purpose.
from pathlib import Path
import json
from agents import RunState
# Pause: serialise the run and stop. No process waits for the human.
state = result.to_state()
Path(f"runs/{run_id}.json").write_text(state.to_string())
# Resume (any process, any time later): rebuild state and continue.
stored = json.loads(Path(f"runs/{run_id}.json").read_text())
state = await RunState.from_json(agent, stored)
state.approve(interruption) # or state.reject(...)
result = await Runner.run(agent, state)Your provider needs to replace the simple file-storage example with an arrangement suitable for your system, and keep a reliable link between the saved run and the approval request. Set a deadline and decide what happens if it is missed.
Step 4 — Give the review a clear owner
A request can go to a person in your business or to an agreed support team. Either way, the person needs time, instructions and authority to decide. Questions outside that authority should go to the manager you nominate.
The example below sketches a request to a reviewer service your provider would need to implement. It does not submit work to hitl.ph. A managed staffing arrangement and the actual connection must be agreed separately.
import requests
def route_to_reviewer(interruption, run_id: str) -> str:
"""Send a pending approval to a managed human reviewer."""
r = requests.post(
"https://reviewer.example.invalid/v1/tasks",
headers={"Authorization": f"Bearer {REVIEWER_API_KEY}"},
json={
"type": "decision.own",
"brief": (
f"An agent wants to call {interruption.name} with "
f"{interruption.arguments}. Approve or decline, one line of reasoning."
),
"context": {"run_id": run_id},
"return": {"mode": "webhook", "url": "https://your-app.com/hooks/hitl"},
},
)
return r.json()["id"]The same business process may also need a phone call, document check or record check. Our setup guide helps prepare the process before implementing a connection.
What to check before using it
- The policy is clear. A reviewer should know which cases they can approve and which need a manager.
- A decline has a reason. The next step should use that explanation, rather than simply asking again.
- Decisions are recorded. Keep who approved what, when and using which information.
- Missing answers have a next step. Agree a deadline and a route for overdue work.
- The technical controls are tested. Your provider should test access, persistence, repeated responses and the behaviour after approval or rejection. The examples above do not implement a complete production service.
Make this work in your business.
A short conversation about your business is a good place to start. Tell us what you have automated, what still needs checking and how often it happens.
You can email us directly at ai@yoonet.io.
Read next
- The Philippines' BPO Industry Is Growing Despite AI
- Cloudflare's Agent Stack: Email, Traces, and Now Wallets
- Human in the Loop Patterns for AI Agents
For your next step, explore the hitl.ph API docs, the business examples and your team options.