Paybond integrates with the OpenAI Agents SDK at the tool input guardrail and invoke boundary — not as model middleware. spend verify runs before side-effecting tools execute; auto-evidence fires after success.
The Python SDK uses the same guardrail and invoke hooks via openai-agents on PyPI.
Install
Install
npm install @paybond/openai-agents @openai/agentsImport from @paybond/openai-agents
import { createPaybondOpenAIAgentsConfig } from "@paybond/openai-agents";- Equivalent subpath on the core package: `@paybond/kit/openai-agents` — use `@paybond/kit` when you need multiple adapters in one app.
- Python: `paybond agent demo openai-agents smoke` requires the optional `openai-agents` extra. Use `pip install "paybond-kit[openai-agents]"`, `pipx install 'paybond-kit[openai-agents]'`, or `pipx inject paybond-kit openai-agents` (when base paybond-kit is already installed).
Recommended wiring
One-liner (sandbox): paybond.instrument({ policy, framework: "openai-agents", tools, sandbox: true }) or paybond.agent({ policy, framework: "openai-agents", tools }) returns guarded tools plus runConfig for Runner.run.
TypeScript
import { tool } from "@openai/agents"; import { z } from "zod"; import { Paybond } from "@paybond/kit"; const paybond = await Paybond.open({ apiKey: process.env.PAYBOND_API_KEY! }); const bookHotelTool = tool({ name: "travel.book_hotel", description: "Book a hotel room", parameters: z.object({ city: z.string(), estimatedPriceCents: z.number().int().nonnegative(), }), execute: async (args) => bookHotel(args), }); const { agentTools: tools, runConfig } = await paybond.instrument({ policy: "./paybond.policy.yaml", // or preset id "travel" framework: "openai-agents", tools: [bookHotelTool, searchWebTool], }); // Runner.run(agent, input, { ...runConfig })
Python
from agents import function_tool from paybond_kit import Paybond paybond = await Paybond.open(api_key=os.environ["PAYBOND_API_KEY"]) @function_tool async def book_hotel(city: str, estimated_price_cents: int) -> str: ... runtime = await paybond.instrument( { "policy": "./paybond.policy.yaml", "framework": "openai-agents", "tools": [book_hotel, search_web_tool], "sandbox": True, } ) tools = runtime.tools run_config = runtime.hooks.run_config # await Runner.run(agent, input, run_config=run_config)
Already bound a run? paybond.wrapTools(run, tools, { framework: "openai-agents" }) / paybond.wrap_tools(run, tools, framework="openai-agents").
See Agent middleware for policy files, registry rules, and tenant isolation.
Advanced / manual wiring
When you need step-by-step control over registry and bind, use both primitives from createPaybondOpenAIAgentsConfig / create_paybond_openai_agents_config:
| Primitive | Purpose |
|---|---|
inputGuardrails / tool_input_guardrails on side-effecting tools | Pre-execution spend verify, deny, HITL hold |
Wrapped invoke / on_invoke_tool | Post-success spend finalize and auto-evidence |
runConfig.toolExecution.preApprovalInputGuardrails / RunConfig(tool_execution=ToolExecutionConfig(pre_approval_tool_input_guardrails=True)) | Enable guardrails before OpenAI approval interruptions |
TypeScript
import { tool } from "@openai/agents"; import { z } from "zod"; import { Paybond, createPaybondToolRegistry } from "@paybond/kit"; import { createPaybondOpenAIAgentsConfig } from "@paybond/kit/openai-agents"; const paybond = await Paybond.open({ apiKey: process.env.PAYBOND_API_KEY! }); const registry = createPaybondToolRegistry({ defaultDeny: true, sideEffecting: { "travel.book_hotel": { spendCents: (args: { estimatedPriceCents: number }) => args.estimatedPriceCents, evidencePreset: "cost_and_completion", evidenceMapper: (result) => ({ status: result.reservation.status === "confirmed" ? "completed" : result.reservation.status, cost_cents: result.reservation.price_cents, }), }, }, }); const run = await paybond.agentRun.bind({ bootstrap: { kind: "sandbox", operation: "travel.book_hotel", requestedSpendCents: 20_000, completionPreset: "cost_and_completion", }, registry, }); const bookHotelTool = tool({ name: "travel.book_hotel", description: "Book a hotel room", parameters: z.object({ city: z.string(), estimatedPriceCents: z.number().int().nonnegative(), }), execute: async (args) => bookHotel(args), }); const { tools, runConfig } = createPaybondOpenAIAgentsConfig(run, [bookHotelTool, searchWebTool]); // Runner.run(agent, input, { ...runConfig })
Python
from paybond_kit import Paybond from paybond_kit.agent.registry import create_paybond_tool_registry from paybond_kit.openai_agents import create_paybond_openai_agents_config paybond = await Paybond.open(api_key=os.environ["PAYBOND_API_KEY"]) registry = create_paybond_tool_registry( { "default_deny": True, "side_effecting": { "travel.book_hotel": { "spend_cents": lambda args: int(args.get("estimatedPriceCents", 0)), "evidence_preset": "cost_and_completion", "evidence_mapper": lambda result, _ctx: { "status": result.get("status"), "cost_cents": result.get("cost_cents"), }, } }, } ) run = await paybond.agent_run.bind( { "bootstrap": { "kind": "sandbox", "operation": "travel.book_hotel", "requested_spend_cents": 20_000, "completion_preset": "cost_and_completion", }, "registry": registry, } ) config = create_paybond_openai_agents_config(run, [book_hotel_tool, search_web_tool]) # Runner.run(agent, input, run_config=config.run_config)
createPaybondOpenAIAgentsConfig / create_paybond_openai_agents_config
- Adds a Paybond input guardrail to each side-effecting registered tool.
- Wraps invoke with
run.interceptor.wrapExecutefor auto-evidence. - Returns
runConfigwith pre-approval input guardrails enabled.
Read-only tools pass through without guardrails or wrapped invoke.
Optional: bridge OpenAI needsApproval
Pass { bridgeNeedsApproval: true } / PaybondOpenAIAgentsAdapterOptions(bridge_needs_approval=True) to also require OpenAI human-in-the-loop approval after Paybond pre-check passes.
Approval hold and retry
When Harbor returns approvalRequired, the input guardrail rejects with a structured message. After operator approval:
run.storeApprovalToken(toolCallId, approvalTokenFromConsole); // Retry Runner.run with the same run binding.
run.store_approval_token(tool_call_id, approval_token_from_console) # Retry Runner.run with the same run binding.
Scaffold and smoke
# TypeScript paybond init agent-middleware --framework openai --out ./paybond-openai-agents.ts # Python paybond init agent-middleware --framework openai --out ./paybond_openai_agents.py paybond agent demo openai-agents smoke \ --operation paid-tool \ --requested-spend-cents 100 \ --evidence-preset cost_and_completion \ --format json
The smoke command runs guardrail pre-check and guarded invoke directly — no OpenAI API key in CI.
Python optional extra: pip install "paybond-kit[openai-agents]". With pipx, quote extras on zsh: pipx install 'paybond-kit[openai-agents]' or pipx inject paybond-kit openai-agents.
Example app (TypeScript): examples/paybond-kit-openai-agents-typescript.
Related
- Agent-agnostic adapter — Python and TypeScript default path
- Mastra adapter — TypeScript-only alternative
- CrewAI adapter — Python-only alternative
- Cloudflare Agents adapter
- Agent middleware —
PaybondAgentRun, registry, interceptor - Vercel AI adapter — alternative TypeScript tool-approval pattern
- Support matrix — install surfaces and smoke commands