Platform guide · OpenAI Agents SDK

Building a refund agent with the OpenAI Agents SDK: tools, guardrails and approvals

In the OpenAI Agents SDK, a refund is a function tool you write, and every control around it is yours to add. Tool input guardrails can block a call before it runs, an approval setting pauses the run for human sign-off, conditional enabling hides the tool, and tracing records each call. Adversarial testing is up to you.

Last updated

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.

FeatureWhat it doesDocumented limit
Tool input guardrail (@tool_input_guardrail)Runs before a function tool executes. Can trip ToolInputGuardrailTripwireTriggeredDoesn’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 runsSame scope as tool input guardrails
Input guardrailChecks the user’s input on the first agent in a chainRuns 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 guardrailChecks the final output of the last agentRuns after the agent completes
needs_approvalTrue always pauses for approval. An async callable decides per call from the run context, parsed arguments and call IDIf arguments cannot be parsed safely, manual approval is required
is_enabledHides the tool from the model when false. Re-checked before a local function tool runsControls 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_approval to 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 RunState storage.

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.

Sources

  1. OpenAI Agents SDK: Tools
  2. OpenAI Agents SDK: Guardrails
  3. OpenAI Agents SDK: Human-in-the-loop
  4. OpenAI Agents SDK: Tracing
  5. OpenAI API docs: Evaluate agent workflows

Related