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

Gateway-only traces are one LLM span per completion (model, prompt, completion, tokens, policy). No tools. No nested agent wraps. No session tree. That is nowhere close to an SDK timeline. Use the Gateway to stop or reshape the provider call. Use the SDK when you need to see what the agent did.

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.

  1. Can you install the SDK on this agent? If yes, go to step 2. If no, use Gateway only.
  2. 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.
  3. Do not run govern and 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?

NoClosed-code, a builder, or raw HTTP

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.

openai.chat
Yes, wrap every callYou own the agent code

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.

agent.run
├─ llm.chat
├─ tool.refund
└─ llm.chat
Yes, some calls you cannot wrapInstrumented agents plus closed traffic

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.

agent.run
├─ llm.chat
openai.chat Gateway

What Applies

TemplateGatewaySDK
Spend CapThis LLM callThis LLM call
Model BoundaryThis LLM callThis LLM call
Loop CapDoes not applyTool calls in this run
Refund Guard / Purchase Guard / High-Risk Tool / Dangerous Shell CommandsDoes not applyThis tool call
Freshness Guard / Retrieval Completeness / Unique Customer Cap / Prompt PinDoes not applySDK only
Detective (Cost Spike, Tool Storm)After traces landAfter 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.

PlatformKnobClient Base URL
OpenAI SDKbase_url / baseURL + default_headershttps://gateway.traccia.ai/openai/v1
Anthropic SDKbase_url / baseURL + default_headershttps://gateway.traccia.ai/anthropic (no /v1)
Gemini SDKhttp_options.base_url / httpOptions.baseUrlhttps://gateway.traccia.ai/google
LangChainChatOpenAI base_url; ChatAnthropic base_urlhttps://gateway.traccia.ai/openai/v1
LiteLLMapi_base / base_url + extra_headershttps://gateway.traccia.ai/openai/v1
Vercel AI SDKcreateOpenAI({ baseURL, headers })https://gateway.traccia.ai/openai/v1
LlamaIndexOpenAI(api_base=, default_headers=)https://gateway.traccia.ai/openai/v1
CrewAILLM(base_url=, extra_headers=)https://gateway.traccia.ai/openai/v1
OpenAI Agents SDKAsyncOpenAI(base_url=) + chat_completionshttps://gateway.traccia.ai/openai/v1
n8nOpenAI credential Base URL, or HTTP Requesthttps://gateway.traccia.ai/openai/v1
HTTPFull URL + headershttps://gateway.traccia.ai/openai/v1/chat/completions

What this Gateway accepts

OpenAI: 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

Azure OpenAI deployment URLs, the OpenAI Responses API (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

HeaderValue
X-Traccia-Api-KeyWorkspace key from Settings → API Keys
X-Traccia-Agent-IdSame 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.

python
import os
from 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 happenedWhat your client gets
Traccia allowed the callThe provider's response
Traccia denied the call403, code policy_denied, whatever the setting
Traccia could not be reached, Block calls503 with Retry-After: 2. The call is not sent to the provider
Traccia could not be reached, Allow callsThe provider's response, without a policy decision
Traccia rejected the API key401 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

The Gateway learns your setting from the calls Traccia answers and keeps it separately for each API key and agent. A change reaches an agent when Traccia next answers one of its calls; answers in the first 5 seconds after you save may still carry the old setting. Until then, that agent's calls follow the setting it last saw. If the Gateway has had no answer for an agent in the last 10 minutes, or since it started, it blocks that agent's calls.

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.

FieldGateway onlySDK
SpansOne LLM span per completion (openai.chat, anthropic.chat, or google.chat)Root plus nested LLM, tool, and agent spans
Prompt and completionOn that LLM span (redacted, truncated)On each LLM span (redacted)
Tools and nested spansNot capturedCaptured
Tokens and costProvider usage on allow. $0 and 0 tokens on hard denySame billed rules
PolicyStamped on that spanStamped 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.