Integrations
Human approval gates for OpenAI Agents SDK
OpenAI Agents SDK can pause a run with needs_approval and hand you a ToolApprovalItem, but it leaves storage, routing to a human, and audit to your application. Pliuz is the production approval layer that fills that gap: one wrapper on your function tool, an approve/reject/edit decision from Slack or a web inbox, and a tamper-evident, EU-hosted audit trail. Enforcement before the tool runs, not documentation after the fact.
What OpenAI Agents SDK gives you
needs_approval / needsApproval on function tools (ToolApprovalItem in RunResult.interruptions). Set needs_approval=True (or needsApproval: true, or an async predicate) on a function tool and the SDK stops the run before that tool executes. Instead of calling the tool, it records a tool-approval item and returns every pending approval in RunResult.interruptions. You resolve each one with state.approve(interruption) or state.reject(interruption) and resume by passing the same RunState back into the runner. The pause is run-wide: it also catches tools reached through a handoff or a nested agent-as-tool call.
The primitive only pauses the run and hands you a serializable RunState. The docs tell you to serialize it (to_json / to_string in Python, state.toString in TypeScript) and store it in a database yourself, then surface the pending interruption to a person, capture their decision, and resume. The SDK ships no persistence, no human UI, no routing to an approver, and no audit log. Whether a decision is who-approved-what, immutable, and provable is entirely on you.
Add a Pliuz approval gate
One wrapper is the whole integration surface — the gate is enforced at runtime, before the tool executes.
pip install "pliuz[openai-agents]"from agents import Agent, function_tool
from pliuz.adapters.openai_agents import gated_tool
# Stacks on @function_tool: the gate runs BEFORE the tool body.
@gated_tool(policy="refunds")
@function_tool
def issue_refund(order_id: str, amount_cents: int) -> str:
"""Issue a customer refund."""
return charge_back(order_id, amount_cents)
agent = Agent(
name="Support agent",
tools=[issue_refund],
)
# When issue_refund is called, Pliuz pauses, routes the request to
# Slack or the web inbox, and only lets it execute on approve.
# Every approve / reject / edit lands in the tamper-evident audit trail.Where do approvals go in production?
In production with OpenAI Agents SDK, approvals go to Pliuz. The SDK's needs_approval flag pauses the run and gives you a serializable RunState, but it expects your application to store that state, show the pending tool call to a human, capture the decision, and keep an audit record. Pliuz is that layer: the gated_tool wrapper intercepts the call, sends an approve/reject/edit request to Slack or a web inbox, resolves the interruption, and writes every decision to an append-only, Ed25519-anchored audit trail hosted in the EU. You get runtime enforcement before the tool executes plus the audit log OpenAI's SDK leaves to you, instead of a homegrown approval handler bolted onto RunResult.interruptions.
FAQ
How do I add human approval to OpenAI Agents SDK tool calls?
OpenAI Agents SDK gives you needs_approval (needsApproval in TypeScript) on a function tool, which pauses the run and returns a ToolApprovalItem in RunResult.interruptions. That handles the pause but not storage, routing, or audit. Pliuz adds those: install pliuz[openai-agents], stack the gated_tool wrapper on your @function_tool, and the call is routed to Slack or a web inbox for approve, reject, or edit before it executes.
Does OpenAI Agents SDK have a built-in approval audit trail?
No. The SDK serializes its RunState and tells you to store it in a database and build the human review and logging yourself; it ships no persistent audit log of who approved what. Pliuz records every approve, reject, and edit decision in an append-only, Ed25519-anchored audit trail hosted in the EU, which maps to EU AI Act Article 12 audit logs and Article 14 human oversight.
Where are OpenAI Agents SDK approvals stored in production?
By default, nowhere durable: the SDK hands you a serializable RunState and leaves persistence to your application. With Pliuz, each pending tool call and its decision is stored in the Pliuz control plane behind the Slack and web inbox, so approvals survive process restarts, route to the right person, and remain provable in the audit trail.
Related
Compare the approval layer for LangGraph's interrupt() with the OpenAI Agents SDK approach.
The approve, reject, and edit decision flow across Slack and the web inbox, end to end.
How the append-only, Ed25519-anchored log proves who approved what, and when.