Gateway
Policy enforcement for agents you cannot put the SDK on. Not a tracing product. Gateway-only traces are one LLM span, nowhere close to an SDK timeline.
The Gateway is a policy enforcement point, not a tracing product. You keep your provider API key. You add two Traccia headers and change the model base_url (or that client's equivalent). Spend Cap and Model Boundary can deny or reshape this call. Get your Traccia API key from app.traccia.ai only.
Not A Tracing Product
SDK, Gateway, Or Both
Start from whether you can install the Traccia SDK on this agent. Policy coverage and trace depth are not the same choice.
- Can you install the SDK on this agent? If yes, go to step 2. If no, use Gateway only.
- Do you need a full timeline (tools, nested spans, Loop Cap)? If yes, use SDK only with govern. If some LLM calls still leave through a client you cannot wrap, add Gateway for those calls only.
- Do not run
governand the Gateway on the same HTTP call. The SDK check wins and the Gateway never sees that request.
Start Here
Can you install the SDK on this agent?
Gateway Only
Policy
Spend Cap and Model Boundary on this LLM call. Loop Cap, tool guards, Freshness Guard, Retrieval Completeness, Unique Customer Cap, and Prompt Pin do not apply.
Trace
One LLM span. Not close to the SDK.
SDK Only
Policy
Spend Cap, Model Boundary, Loop Cap, tool guards, Freshness Guard, Retrieval Completeness, Unique Customer Cap, and Prompt Pin on this run.
Trace
Full timeline: root, nested LLM spans, tools, prompts, completions.
SDK + Gateway
Policy
SDK governs instrumented runs. Gateway governs the calls it proxies. Not both on the same call.
Trace
SDK timeline plus one Gateway LLM span. Send a W3C traceparent to nest it.
What Applies
| Template | Gateway | SDK |
|---|---|---|
| Spend Cap | This LLM call | This LLM call |
| Model Boundary | This LLM call | This LLM call |
| Loop Cap | Does not apply | Tool calls in this run |
| Refund Guard / Purchase Guard / High-Risk Tool / Dangerous Shell Commands | Does not apply | This tool call |
| Freshness Guard / Retrieval Completeness / Unique Customer Cap / Prompt Pin | Does not apply | SDK only |
| Detective (Cost Spike, Tool Storm) | After traces land | After traces land |
Closed-code products must let you set a custom OpenAI, Anthropic, or Gemini URL plus extra headers. If there is no URL knob, the Gateway cannot see the traffic.
Platforms
Each page uses that client's real knob names and the exact URL the Gateway implements. The Anthropic URL is not the same on every SDK. OpenAI Chat Completions is not the OpenAI Responses API.
| Platform | Knob | Client Base URL |
|---|---|---|
| OpenAI SDK | base_url / baseURL + default_headers | https://gateway.traccia.ai/openai/v1 |
| Anthropic SDK | base_url / baseURL + default_headers | https://gateway.traccia.ai/anthropic (no /v1) |
| Gemini SDK | http_options.base_url / httpOptions.baseUrl | https://gateway.traccia.ai/google |
| LangChain | ChatOpenAI base_url; ChatAnthropic base_url | https://gateway.traccia.ai/openai/v1 |
| LiteLLM | api_base / base_url + extra_headers | https://gateway.traccia.ai/openai/v1 |
| Vercel AI SDK | createOpenAI({ baseURL, headers }) | https://gateway.traccia.ai/openai/v1 |
| LlamaIndex | OpenAI(api_base=, default_headers=) | https://gateway.traccia.ai/openai/v1 |
| CrewAI | LLM(base_url=, extra_headers=) | https://gateway.traccia.ai/openai/v1 |
| OpenAI Agents SDK | AsyncOpenAI(base_url=) + chat_completions | https://gateway.traccia.ai/openai/v1 |
| n8n | OpenAI credential Base URL, or HTTP Request | https://gateway.traccia.ai/openai/v1 |
| HTTP | Full URL + headers | https://gateway.traccia.ai/openai/v1/chat/completions |
What this Gateway accepts
POST /openai/v1/chat/completions and GET /openai/v1/models, also under /agents/{agent_id}/v1 and /openai/agents/{agent_id}/v1 with the agent ID in the path. Anthropic: POST /anthropic/v1/messages. Gemini Developer API: POST /google/{v1beta|v1}/models/{model}:generateContent and :streamGenerateContent. Embeddings, the OpenAI Responses API, Assistants, Azure OpenAI paths, Vertex AI paths, and Anthropic Batches are not this Gateway. Those requests 404.These Paths Are Not This Gateway
POST /v1/responses), embeddings, Assistants, Realtime, Anthropic Batches, Anthropic /v1/complete, and Vertex AI Gemini (vertexai=True) are not implemented. Those requests 404. The client must also send both Traccia headers. A URL knob alone is not enough.Headers
| Header | Value |
|---|---|
| X-Traccia-Api-Key | Workspace key from Settings → API Keys |
| X-Traccia-Agent-Id | Same Agent ID you would pass to init |
Keep the provider API key on the client. The Gateway forwards it and does not store it. Missing Traccia key returns 401. Missing agent ID returns 400. For OpenAI-compatible clients that allow only one extra header, such as n8n, put the agent ID in the URL instead: https://gateway.traccia.ai/agents/<agent-id>/v1. If both are set, the header wins.
Native SDK Snippets
These match Integrations → Gateway in the app. Framework wrappers (LangChain, LiteLLM, Vercel AI SDK, n8n) have their own pages because their URL rules differ.
import osfrom openai import OpenAI
client = OpenAI( api_key=os.environ["OPENAI_API_KEY"], base_url="https://gateway.traccia.ai/openai/v1", default_headers={ "X-Traccia-Api-Key": os.environ["TRACCIA_API_KEY"], "X-Traccia-Agent-Id": "billing-bot", },)When Traccia Can't Be Reached
Before each call the Gateway asks Traccia whether it may go ahead. If Traccia does not answer (a timeout, a connection error, a 5xx, or a reply the Gateway cannot use), the Gateway follows your setting: Block calls (the default) or Allow calls. Choose it in the app under Settings → Policy Enforcement: an organization default, and optionally a different choice per workspace, for example allow in development and block in production. Each call follows the setting of the workspace its Traccia API key belongs to.
| What happened | What your client gets |
|---|---|
| Traccia allowed the call | The provider's response |
| Traccia denied the call | 403, code policy_denied, whatever the setting |
| Traccia could not be reached, Block calls | 503 with Retry-After: 2. The call is not sent to the provider |
| Traccia could not be reached, Allow calls | The provider's response, without a policy decision |
| Traccia rejected the API key | 401 invalid_api_key or 403 permission_denied, whatever the setting |
Every Gateway error carries X-Traccia-Error-Code (policy_unavailable for the 503). The body is in your provider's error format: OpenAI also has code: policy_unavailable, Anthropic has type: api_error, and Gemini has status: UNAVAILABLE. Retry after Retry-After seconds.
When A Change Takes Effect
What You See In Traces
Gateway-only traffic still writes a trace, including allows with no policy match. Treat that as an enforcement receipt, not observability. It is one LLM span, not an SDK timeline, and it will not grow into one. You will see model, prompt, completion, billed tokens, and policy on that span. You will not see tools or nested agent wraps. Send a W3C traceparent if you already wrap the run with the SDK so the Gateway span nests under it.
| Field | Gateway only | SDK |
|---|---|---|
| Spans | One LLM span per completion (openai.chat, anthropic.chat, or google.chat) | Root plus nested LLM, tool, and agent spans |
| Prompt and completion | On that LLM span (redacted, truncated) | On each LLM span (redacted) |
| Tools and nested spans | Not captured | Captured |
| Tokens and cost | Provider usage on allow. $0 and 0 tokens on hard deny | Same billed rules |
| Policy | Stamped on that span | Stamped on the LLM span |
Hard deny never bills. The attempted prompt is kept; there is no completion and no billed usage. PDP estimates stay on traccia.policy.estimated_input_tokens and are not usage. Detective or Warn would-have still ran the provider, so cost and tokens stay billed. Gateway traces look thinner than SDK traces because the Gateway only sees this provider call, not the rest of the agent. If you need the SDK timeline, install the SDK. The Gateway will not fill that gap.
Next Steps
© 2026 Traccia.