Skip to content
/docs

OpenAI Agents Integration

Protect OpenAI Agents tools with Veto guard checks and wrapped execution.

The local TypeScript SDK does not ship a dedicated veto-sdk/integrations/openai-agents package.

Use the current SDK primitives:

  • veto.guard(toolName, args, context) for preflight checks
  • veto.wrap(...) for executable TypeScript tools
  • ToolCallDeniedError for policy-denied execution
  • Veto.charter to compile and review policies for registered tools

TypeScript tool guard

import { ToolCallDeniedError, Veto } from "veto-sdk";

const veto = await Veto.init();

const tools = veto.wrap([
  {
    name: "send_email",
    description: "Send an email to a customer",
    handler: async (args: { to: string; body: string }) => {
      return { sent: true, to: args.to };
    },
  },
], {
  context: {
    agentId: "openai-agent",
    sessionId: "support-session",
  },
});

try {
  const result = await tools[0].handler({
    to: "customer@example.com",
    body: "Account details...",
  });
  console.log(result);
} catch (error) {
  if (error instanceof ToolCallDeniedError) {
    console.log(`Blocked by Veto: ${error.reason ?? error.decision}`);
  } else {
    throw error;
  }
}

Preflight guard

Use preflight when your framework gives you the proposed tool call before execution:

const guard = await veto.guard("send_email", {
  to: "customer@example.com",
  body: "Account details...",
}, {
  agentId: "openai-agent",
  sessionId: "support-session",
});

if (guard.decision !== "allow") {
  throw new Error(guard.reason ?? `Veto returned ${guard.decision}`);
}

Python status

This repository currently publishes the TypeScript SDK. For Python OpenAI Agents, call the Veto API directly from a tool guardrail until a Python package is published:

import httpx
import os

async def veto_guard(tool_name: str, args: dict):
    response = await httpx.AsyncClient().post(
        "https://api.veto.so/v1/validate",
        headers={"X-Veto-API-Key": os.environ["VETO_API_KEY"]},
        json={"toolName": tool_name, "arguments": args},
        timeout=10,
    )
    response.raise_for_status()
    decision = response.json()
    if decision["decision"] != "allow":
        raise RuntimeError(decision.get("reason") or decision["decision"])

Charter loop

Register the tools once, then let Charter draft policy for them:

await veto.registerTools([
  {
    name: "send_email",
    description: "Send an email to a customer",
    parameters: [
      { name: "to", type: "string", required: true },
      { name: "body", type: "string", required: true },
    ],
  },
]);

const res = await fetch("https://api.veto.so/v1/charter/turn", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Veto-API-Key": process.env.VETO_API_KEY!,
  },
  body: JSON.stringify({
    message: "Customer emails that include account details need support lead approval.",
    mode: "compile",
    surface: "api",
  }),
});
const draft = await res.json();

console.log(draft.verification.linkUrl);

Policy-changing Charter results require verification before anything goes live.