Sanction + Vercel AI SDK
Route your AI SDK calls through Sanction's gateway to meter every token and cap spend — per agent, per tenant, across providers — without changing how you call the model. You keep your own provider key; Sanction just sits in front of it.
The whole integration is two lines of config: point the provider's baseURL at
Sanction and add the x-sanction-key header. Everything else is normal AI SDK.
Install
npm i ai @ai-sdk/openai-compatible @ai-sdk/anthropic @ai-sdk/google
You need a Sanction agent key (pxy_…) — one per tenant. See the
Integration Runbook to provision them. In these examples
AGENT_KEY is that pxy_… key.
Runnable version:
examples/vercel-ai-sdk/runs the metered call + a real authorize decision end to end in ~2 minutes (examples/setup.shprovisions the wallet for you).
OpenAI
Use @ai-sdk/openai-compatible, not the native @ai-sdk/openai — see the note
at the bottom; it's the difference between Sanction metering your calls and not.
import { createOpenAICompatible } from "@ai-sdk/openai-compatible"
import { generateText } from "ai"
const openai = createOpenAICompatible({
name: "openai",
apiKey: process.env.OPENAI_API_KEY, // your key — forwarded upstream
baseURL: "https://getsanction.com/api/gateway/openai/v1",
headers: { "x-sanction-key": process.env.AGENT_KEY! }, // meters + caps
includeUsage: true, // so streamed calls are metered too
})
const { text } = await generateText({
model: openai("gpt-4o-mini"),
prompt: "Hello from Sanction",
})
Anthropic
import { createAnthropic } from "@ai-sdk/anthropic"
import { generateText } from "ai"
const anthropic = createAnthropic({
apiKey: process.env.ANTHROPIC_API_KEY, // your key — forwarded
baseURL: "https://getsanction.com/api/gateway/anthropic/v1", // AI SDK appends /messages
headers: { "x-sanction-key": process.env.AGENT_KEY! },
})
const { text } = await generateText({
model: anthropic("claude-haiku-4-5-20251001"),
prompt: "Hello from Sanction",
})
Google (Gemini)
import { createGoogleGenerativeAI } from "@ai-sdk/google"
import { generateText } from "ai"
const google = createGoogleGenerativeAI({
apiKey: process.env.GEMINI_API_KEY, // your key — forwarded
baseURL: "https://getsanction.com/api/gateway/gemini/v1beta",
headers: { "x-sanction-key": process.env.AGENT_KEY! },
})
const { text } = await generateText({
model: google("gemini-2.5-flash"),
prompt: "Hello from Sanction",
})
Multi-tenant: one provider, per-request tenant key
For a platform with many tenants, create one provider instance (your provider
key) and pass each tenant's x-sanction-key per request — no need to rebuild
the client per tenant:
const openai = createOpenAICompatible({
name: "openai",
apiKey: process.env.OPENAI_API_KEY,
baseURL: "https://getsanction.com/api/gateway/openai/v1",
includeUsage: true,
})
async function runForTenant(tenantAgentKey: string, prompt: string) {
return generateText({
model: openai("gpt-4o-mini"),
prompt,
headers: { "x-sanction-key": tenantAgentKey }, // per-tenant, per call
})
}
streamText takes the same per-call headers. Map tenantId → agentKey in your
resolver (see the runbook).
When the budget is hit
When a tenant's daily token budget is exhausted, the gateway returns 402 before the call reaches the provider — the AI SDK surfaces it as a thrown error. That's the governance payoff: your code learns the cap, it doesn't silently overspend.
import { APICallError } from "ai"
try {
await generateText({ model: openai("gpt-4o-mini"), prompt, headers: { "x-sanction-key": tenantAgentKey } })
} catch (err) {
if (APICallError.isInstance(err) && err.statusCode === 402) {
// tenant is over budget — degrade gracefully, queue, or notify the owner
} else {
throw err
}
}
Set budgets and thresholds with the control-plane API — see the Integration Runbook.
OpenAI: compatible vs. native provider
Either works — Sanction meters both OpenAI's Chat Completions (what
@ai-sdk/openai-compatible uses) and the Responses API (what the native
@ai-sdk/openai provider uses by default). We show the compatible provider above
because it's explicit about includeUsage for streaming, but the native
@ai-sdk/openai works the same way — just set baseURL + headers on
createOpenAI(...). Anthropic and Google's native providers meter correctly too.
Degradation: the gateway is in-path. If you need a fallback when it's slow or down, wrap these calls so an error falls back to the provider's own base URL — you already hold the provider key. See the runbook's circuit-breaker section.