Base URLs & request surfaces
The gateway serves three request surfaces, because the clients worth firewalling do not agree on one. Which surface you use is decided by your client, not by you — and it determines the shape of your base URL.
/v1. The OpenAI SDK expects it
in the base URL. The Anthropic SDK appends it itself, so its base URL must
stop at the organisation. This trips up nearly everyone who configures by hand —
vulnetix ai-firewall install gets it right for you.The two shapes
| Your client | Base URL |
|---|---|
| OpenAI SDKs, LangChain, LlamaIndex, Codex, curl | https://guardrails.vulnetix.com/{provider}/{orgUuid}/v1 |
| Anthropic SDKs, Claude Code, Claude Desktop | https://guardrails.vulnetix.com/anthropic/{orgUuid} — no /v1 |
{provider} is the provider slug (openai, anthropic, openrouter, groq, …
— see the provider catalog). {orgUuid} is your
organisation UUID, from the
dashboard.
Both are URL components on purpose: the entire configuration fits into the two
knobs every LLM client supports — base_url and api_key — so no client needs to
send a custom header.
The three surfaces
| Surface | Route | Spoken by |
|---|---|---|
chat | POST /v1/chat/completions | OpenAI SDKs, LangChain, Cursor, Continue, Aider, curl — most things |
responses | POST /v1/responses | Codex CLI (its config accepts no other wire_api) |
messages | POST /v1/messages | Anthropic SDKs, Claude Code, Claude Desktop |
Guardrails apply identically across all three. A rule you write once covers a curl, a Codex turn, and a Claude Code tool result alike — you never write a policy per surface.
Not every provider serves every surface. Which ones a provider serves is part of its catalog entry:
| Provider | Surfaces |
|---|---|
openai | chat, responses |
anthropic | messages |
| everything else | chat |
When you get it wrong
Asking a provider for a surface it does not serve is a 404, and the message
tells you what to use instead. This is deliberate: a misconfigured base URL should
not surface as a confusing 401 or a timeout.
{
"type": "error",
"error": {
"type": "not_found_error",
"code": "unsupported_api",
"message": "provider groq does not support the Anthropic Messages API (POST /v1/messages). Use POST /v1/chat/completions for groq, or point your Anthropic client at a provider that serves the Messages API: https://guardrails.vulnetix.com/anthropic/{orgUuid} (note: no /v1 suffix — the Anthropic SDK appends it)"
}
}
The CLI catches the same class of mistake locally, before you ever send a request —
it is the wire_unsupported check in
vulnetix ai-firewall status.
Errors come back in your client’s dialect
The error envelope follows the route you called, never the provider. An Anthropic
SDK that hits /v1/messages gets an Anthropic-shaped error even when the failure
is “this provider is not an Anthropic provider” — because that is the only shape it
can render. Given an OpenAI-shaped body, Claude Code would print a bare
API Error: 403 with no detail, and every guardrail block would be unreadable.
The full taxonomy, in both shapes, is in the error reference.
Model names pass through untouched
The gateway does not rewrite, prefix, or normalise the model you send. Use
whatever naming the provider itself uses:
| Provider | Model string |
|---|---|
openai | gpt-4o-mini |
openrouter | openai/gpt-4o-mini |
anthropic | claude-sonnet-5 |
Getting this wrong produces the provider’s own “unknown model” error, relayed verbatim — not one of ours.