How does a refund tool work in the Agents SDK?
A function tool is a decorated Python function (function_tool). The SDK builds the tool’s input schema from the function signature with Python’s inspect module and Pydantic, takes argument descriptions from the docstring, and includes Pydantic Field constraints in both the schema and validation.
Schema constraints check shape, not policy. Illustrative: Field(gt=0, le=500) on the amount rejects $900, but it cannot know the order total was $40 or that the order was refunded yesterday. Those checks belong in the tool body, against your order system.
By default a tool error is reported back to the model as a message, so the model may retry or explain. Passing failure_error_function=None re-raises errors to your code instead. Async tools accept a timeout. The default behavior returns a timeout message to the model.
Which guardrails and approvals can you add?
The SDK gives you hooks at the agent, tool and approval level. Each has a documented scope.
| Feature | What it does | Documented limit |
|---|---|---|
Tool input guardrail (@tool_input_guardrail) | Runs before a function tool executes. Can trip ToolInputGuardrailTripwireTriggered | Doesn’t cover hosted tools, built-in execution tools, handoffs or Agent.as_tool() |
Tool output guardrail (@tool_output_guardrail) | Checks a tool’s result after it runs | Same scope as tool input guardrails |
| Input guardrail | Checks the user’s input on the first agent in a chain | Runs in parallel by default, so the agent may already have run tools before a tripwire cancels it. Set run_in_parallel=False to block first |
| Output guardrail | Checks the final output of the last agent | Runs after the agent completes |
needs_approval | True always pauses for approval. An async callable decides per call from the run context, parsed arguments and call ID | If arguments cannot be parsed safely, manual approval is required |
is_enabled | Hides the tool from the model when false. Re-checked before a local function tool runs | Controls visibility only. The docs say it does not replace argument-level authorization |
How does human approval work for a refund call?
When a tool needs approval, the run pauses and result.interruptions lists ToolApprovalItem entries with the tool name and arguments. Convert the result with result.to_state(), call state.approve(...) or state.reject(...), then resume with Runner.run(agent, state).
RunState serializes with to_json() for long waits. The docs warn that RunState.from_json() doesn’t authenticate the snapshot or the person submitting it: keep snapshots in server-controlled storage, authenticate reviewers, and load pending items with state.get_interruptions() rather than trusting client-supplied data. Otherwise a crafted approval becomes the attack.
What does the SDK give you for testing?
Tracing is on by default and records spans for function tool calls, guardrails and handoffs, viewable in the OpenAI Traces dashboard. trace_include_sensitive_data defaults to true, so a refund agent’s traces can hold customer and order data unless you turn it off. Tracing is unavailable to organizations under a Zero Data Retention policy.
OpenAI’s agent evals guidance builds on traces: graders score traces against structured criteria to find regressions, then you move to datasets and eval runs. The guide doesn’t describe simulated customers or adversarial testing, so you supply the conversations: scripted or model-driven customers that push your agent in a test environment.
What to test before giving your agent refund authority
A checklist for a self-built refund agent:
- Set
needs_approvalto a callable that pauses refunds above your threshold, and test amounts on both sides of it. - Put eligibility, order-total and already-refunded checks in the tool body or your API, not only in the schema.
- Use a blocking input guardrail, or a tool input guardrail, if a check must run before any tool call.
- Make the refund call idempotent per order so a retried tool call cannot refund twice.
- Run customers who claim prior approval (social engineering), split one refund into several small ones (multi-turn escalation) and paste fake policy text (indirect prompt injection).
- If refunds go through a handoff or an agent used as a tool, confirm which guardrails still apply, since tool guardrails skip both.
- Verify the approval path itself: reviewer authentication and server-side
RunStatestorage.
Questions
Can I require approval only above a refund amount?
Yes. needs_approval accepts an async callable that receives the run context, the parsed tool parameters and the call ID, so it can return true only when the amount is above your threshold.
Do input guardrails stop a refund tool from running?
Only in blocking mode. By default, input guardrails run in parallel with the agent, which may already have used tokens or run tools before the tripwire cancels it. Set run_in_parallel=False, or use a tool input guardrail on the refund tool itself.
Do tool guardrails apply to handoffs?
No. The SDK docs say tool guardrails cover function tools, not hosted tools, built-in execution tools, handoffs or Agent.as_tool(), because handoffs run through a separate pipeline.
OpenAI is a trademark of its owner. This guide is independent and not affiliated with or endorsed by OpenAI.
Free checklist
Get the guardrail checklist for agents that move money.
A one-page PDF of the controls and the test for each. Download it right away, and we email you the link.
Sources
- OpenAI Agents SDK: Tools
- OpenAI Agents SDK: Guardrails
- OpenAI Agents SDK: Human-in-the-loop
- OpenAI Agents SDK: Tracing
- OpenAI API docs: Evaluate agent workflows