OpenAI runtime authorization

Wrap OpenAI tool calls with Veto. Each governed tool call is evaluated before dispatch: allow, review, or deny, with an exportable decision record per governed decision.

The problem with OpenAI tool calls

OpenAI's function calling lets GPT models trigger real actions in your system: send emails, process payments, modify databases, call external APIs. The model decides what to call, generates the arguments, and your code executes it. There is no guardrail step between the model's decision and your code's execution.

OpenAI's Agents SDK includes input and output guardrails, but these operate on the conversation level, not the tool level. They can check whether a user's message is appropriate, but they do not enforce "this specific function call with these specific arguments should be blocked." Enforcement is not a model problem. It is an infrastructure problem.

No tool-level auth

OpenAI's Agents SDK guardrails check input and output messages, not individual function calls. A blocked message stops the agent entirely, not a single tool call.

Hallucinated arguments

GPT models can hallucinate function arguments: wrong email addresses, inflated amounts, nonexistent table names. Pydantic validation catches type errors, not business logic violations.

No decision record

Neither the Chat Completions API nor the Agents SDK log tool calls with decision context. For SOC 2, HIPAA, or financial compliance, you need to know what was attempted, what was allowed, and what was blocked.

Before and after Veto

The left tab shows standard OpenAI function calling. The model returns tool_calls, your code executes them unconditionally. The right tab adds Veto. Same agent, same tools, each governed call evaluated against policy first.

ts
import OpenAI from 'openai'

const openai = new OpenAI()

const tools = [
  {
    type: 'function',
    function: {
      name: 'send_email',
      description: 'Send an email',
      parameters: {
        type: 'object',
        properties: {
          to: { type: 'string' },
          subject: { type: 'string' },
          body: { type: 'string' },
        },
        required: ['to', 'subject', 'body'],
      },
    },
  },
  {
    type: 'function',
    function: {
      name: 'process_payment',
      description: 'Process a payment',
      parameters: {
        type: 'object',
        properties: {
          amount: { type: 'number' },
          recipient: { type: 'string' },
        },
        required: ['amount', 'recipient'],
      },
    },
  },
]

const response = await openai.chat.completions.create({
  model: process.env.OPENAI_MODEL!,
  messages: [{ role: 'user', content: userMessage }],
  tools,
})

// GPT says "call process_payment with amount: 50000"
// Your code does it. No policy. No limit. No approval.
const toolCall = response.choices[0].message.tool_calls?.[0]
if (toolCall) {
  const args = JSON.parse(toolCall.function.arguments)
  await executeTool(toolCall.function.name, args)
}

Policy configuration

Define guardrail rules in declarative YAML. Version control alongside your code. Prompt engineering is not the enforcement mechanism. Policies apply at the governed tool boundary, independent of the model response.

veto/policies.yaml
rules:
  - name: block_competitor_emails
    description: Block emails to competitor domains
    tool: send_email
    when: args.to.endsWith("@competitor.invalid")
    action: deny
    message: "Cannot send emails to competitor domains"

  - name: approve_large_payments
    description: Require approval for payments over $500
    tool: process_payment
    when: args.amount > 500
    action: require_approval
    approvers: [finance-team]
    timeout: 30m

  - name: block_after_hours_payments
    description: No payments outside business hours
    tool: process_payment
    when: context.time.hour < 9 || context.time.hour > 17
    action: deny
    message: "Payments require business hours (9am-5pm)"

  - name: daily_payment_cap
    description: Cap total daily payments at $10,000
    tool: process_payment
    when: context.daily_total + args.amount > 10000
    action: deny
    message: "Daily payment cap of $10,000 reached"

First governed call

1

Install the SDK

npm install veto-sdk openai
2

Define policies

Create veto/policies.yaml with rules for each tool. Match on tool name, constrain arguments, set actions.

3

Validate before executing

Call veto.guard() in your tool handler. Check the decision. Execute only if allowed.

Supported models

Veto works with OpenAI models that emit tool calls through the API. Keep the model compatibility check in CI because provider catalogs change.

Tool-calling models
Reasoning paths
Fast chat paths
Code-agent paths

What Veto covers for OpenAI agents

Function allowlists

Allowlist which functions the agent can call. Block everything else by default. Different rules per environment, user role, or time of day.

Argument validation

Constrain function arguments by pattern, range, or business rule. Block emails to competitor domains. Cap payment amounts. Restrict SQL to read-only.

Approval workflows

Route sensitive function calls to human approval queues. Approvers get configured approval channel or email notifications with the function name, arguments, policy, and context.

Decision records

Each guarded function call can be recorded with name, arguments, decision, and timestamp. Queryable via API or workspace. Export for SOC 2 and evidence reporting.

Frequently asked questions

How is Veto different from OpenAI's built-in guardrails?
OpenAI's Agents SDK guardrails are input and output guards: they check the user's message or the agent's final response. Veto operates at the function call level. It evaluates each individual tool invocation against your policies before the function runs. They complement each other: use OpenAI guardrails for content screening, Veto for tool-level enforcement.
Does this work with the Responses API and Assistants API?
Yes. Veto validates tool calls regardless of which OpenAI API produced them. Whether you use Chat Completions, the Responses API, or the Assistants API, the integration point is the same: validate before executing the function.
What happens when a function call is blocked?
You control the behavior. Return an error message to the model so it can adjust. Return a fallback value. Or raise an exception. All blocked calls are recorded with decision context including the denial reason.
Does Veto add latency?
Policy evaluation stays in-process before dispatch. Model inference remains the slower path. Human approval workflows pause until a reviewer responds, but standard allow/block decisions stay on the local decision path.

Related integrations

Wrap one OpenAI tool path and inspect the decision record.