Developer documentation
Build on the OpenBricks AI Gateway
One OpenAI- and Anthropic-compatible endpoint for every model your organisation allows, and one MCP endpoint for every business tool. Every request is attributed, checked against your policies and logged.
Overview
OpenBricks sits between your applications, agents and employees and the AI providers and tools they use. Your code talks to OpenBricks with the SDKs it already uses; OpenBricks picks the provider key, enforces your organisation's limits and policies, streams the response back and records who used which model, at what cost.
| Service | URL |
|---|---|
| AI Gateway (model requests and MCP) | https://api.platform.openbricks.ai |
| Web app (keys, usage, policies) | https://platform.openbricks.ai |
api.platform.openbricks.ai and platform.openbricks.ai. Your web app shows the exact gateway URL under My keys.Quickstart
1. Get a key
Sign in to the web app and open My keys. If you belong to a team with a team key, your personal member key is created the first time you ask for it, and you can copy or rotate it at any time. Shared keys for applications and service accounts are issued by an administrator under Governance.
2. Send a request
The gateway speaks the OpenAI API. Set the base URL to the gateway and use your key as the API key.
curl https://api.platform.openbricks.ai/v1/chat/completions \
-H "Authorization: Bearer $OPENBRICKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}'from openai import OpenAI
client = OpenAI(base_url="https://api.platform.openbricks.ai/v1", api_key="YOUR_KEY")
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content)import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://api.platform.openbricks.ai/v1", apiKey: process.env.OPENBRICKS_API_KEY });
const response = await client.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Hello" }],
});
console.log(response.choices[0].message.content);Streaming works the same way as with OpenAI: pass stream: true. Use a model your key allows — see Models and allowlists.
Keys and attribution
Every gateway key belongs to exactly one owner in your organisation, and every request is attributed from the key itself — never from a header the client sends. That is what makes usage and cost reports reliable.
| Key | Owner | Typical use |
|---|---|---|
| Member key | One person, under a team key | Personal use, Claude Code, local scripts |
| Personal key | One person, in a team | Individual developers |
| Service account key | A named bot or pipeline, in a team | Production applications and agents |
| Team key | A team | A policy: allowed providers and models, budget, rate limit and per-member caps |
| Department key | A whole department | Shared access for a department |
Keys are shown once when they are created, except member keys, which their owner can view again. Requests are attributed to the key's department, team and person or service account.
End users, traces and metadata
Applications that serve many users through one shared key can say who a request is for. Send X-Gateway-User, OpenAI's user body field, or _user in the metadata header. It is recorded as the request's end user. It is reported by your client and not verified, and your organisation's log redaction applies to it — prefer an opaque user id over an email address.
curl https://api.platform.openbricks.ai/v1/chat/completions \
-H "Authorization: Bearer $OPENBRICKS_API_KEY" \
-H "X-Gateway-Trace-Id: checkout-4821" \
-H 'X-Gateway-Metadata: {"_user": "u_1842", "feature": "summaries"}' \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Summarise this ticket"}]}'Responses return the trace id in X-Gateway-Trace-Id.
Endpoints
| Route | What it does |
|---|---|
POST /v1/chat/completions | OpenAI Chat Completions, streaming and non-streaming, with key checks, limits, the PII guard, routing configs and logging. |
POST /v1/embeddings | OpenAI Embeddings through the same pipeline, without streaming. Vectors are never stored. |
POST /v1/messages | Anthropic Messages API for Claude Code and the Anthropic SDKs, streaming included. Errors use Anthropic's format. |
POST /v1/messages/count_tokens | Token counting, passed through to the provider. Not logged or billed. |
GET /v1/models | The models your key may use. Not logged, rate limited or billed. |
/<server>/mcp | The MCP Gateway for one of your organisation's MCP servers (Streamable HTTP). |
Anthropic and Claude Code
For Claude Code and the Anthropic SDKs, set the base URL to the gateway without /v1 and use your key. Requests are served by your organisation's Anthropic or OpenRouter provider keys, including the managed OpenBricks AI provider; Claude model names are translated for OpenRouter automatically.
export ANTHROPIC_BASE_URL=https://api.platform.openbricks.ai
export ANTHROPIC_AUTH_TOKEN=YOUR_KEY
export ANTHROPIC_API_KEY="" # must be empty, or Claude Code sends it instead
claudecurl https://api.platform.openbricks.ai/v1/messages \
-H "x-api-key: $OPENBRICKS_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{"model": "claude-sonnet-4-5", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello"}]}'import anthropic
client = anthropic.Anthropic(base_url="https://api.platform.openbricks.ai", api_key="YOUR_KEY")
message = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
print(message.content[0].text)claude-sonnet-4* and claude-haiku-4*. A model that isn't allowed is refused with 403, which Claude Code shows as "Failed to authenticate" together with the gateway's message.anthropic-version and anthropic-beta headers are forwarded. Token counting is available with Anthropic provider keys; OpenRouter has no token-counting endpoint, so count_tokens returns 404 there.
Models and allowlists
A team key sets which providers and models its members may use. GET /v1/models with your key returns exactly that list, and My keys shows it as "Available models". Requests for a model or provider your key doesn't allow are refused with 403 before any provider is called.
- Allowlists accept exact model names or prefixes ending in
*, such asgpt-4o*oranthropic/*. - If you name no provider and your organisation's default isn't allowed for your key, the first allowed provider serves the request.
- With an OpenRouter key, a team key can allow
openrouter/auto. The auto router then picks among the key's other allowed models only, on/v1/chat/completions. Logs and cost record the model actually chosen.
Routing configs
Send a routing config in X-Gateway-Config, either as inline JSON or as the id of a config saved in your organisation. The format is Portkey's config schema, so existing Portkey configs work unchanged.
| Field | Meaning |
|---|---|
strategy.mode | single, fallback or loadbalance |
strategy.on_status_codes | For fallback: which upstream status codes move on to the next target |
targets | Nested configs, tried in order (fallback) or chosen by weight (loadbalance) |
virtual_key | The name (slug) of one of your organisation's provider keys |
provider | Use your organisation's key for this provider |
weight | Load-balancing weight of a target (default 1) |
retry | {"attempts": n, "on_status_codes": [...]}; default codes 429, 500, 502, 503 and 504 |
override_params | Request parameters to set for a target, such as model |
request_timeout | Timeout in milliseconds |
{
"strategy": { "mode": "fallback" },
"retry": { "attempts": 2 },
"targets": [
{ "virtual_key": "openai-prod", "override_params": { "model": "gpt-4o" } },
{ "virtual_key": "openrouter", "override_params": { "model": "openai/gpt-4o" } }
]
}{
"strategy": { "mode": "loadbalance" },
"targets": [
{ "virtual_key": "azure-openai", "weight": 0.8 },
{ "virtual_key": "openai-prod", "weight": 0.2 }
]
}Retries and fallbacks happen only before the first byte of the response, so a streamed answer is never duplicated. Fallback targets may use different providers and keys of your organisation. Responses report retries in X-Gateway-Retry-Attempt-Count.
Choosing a provider key without a config
X-Gateway-Provider-Key: <slug>picks one of your organisation's provider keys by name.- Otherwise
X-Gateway-Provider: <provider>picks your organisation's key for that provider. - Otherwise your organisation's default key is used.
Raw provider API keys sent by clients are never accepted: provider keys live encrypted in the gateway, not in your code.
Headers
Every request header has a neutral name and a Portkey-compatible name. If both are sent, the x-portkey-* header wins.
| Purpose | Header | Portkey-compatible |
|---|---|---|
| Gateway key | Authorization: Bearer <key> | x-portkey-api-key |
| Provider key by name | X-Gateway-Provider-Key | x-portkey-virtual-key |
| Provider | X-Gateway-Provider | x-portkey-provider |
| Routing config (JSON or saved id) | X-Gateway-Config | x-portkey-config |
| Trace id | X-Gateway-Trace-Id | x-portkey-trace-id |
| Metadata (JSON) | X-Gateway-Metadata | x-portkey-metadata |
| End user | X-Gateway-User | — |
For the Anthropic endpoint, the key can also go in x-api-key. Response headers: X-Gateway-Trace-Id, X-Gateway-Retry-Attempt-Count and, when the PII guard masked something, X-Pii-Redacted.
Limits and errors
Limits stack: a request must pass the limits of its key, team, department and organisation. Rate limits count requests per minute; budgets are monthly, in USD, per UTC calendar month. Budgets are soft — spend is added when a response completes, so requests already in flight when a budget is crossed still finish.
| Status | Type | Code | Meaning |
|---|---|---|---|
429 | rate_limit_error | key_rate_limit, team_rate_limit, team_key_rate_limit, department_rate_limit, org_rate_limit | Too many requests at that level. Wait for Retry-After. |
429 | insufficient_quota | key_budget, team_budget, team_key_budget, department_budget, org_budget | Monthly budget used up. Retry-After points to the start of next month. |
429 | insufficient_quota | org_request_quota, org_mcp_call_quota | Your plan's monthly requests or MCP tool calls are used up. |
429 | insufficient_quota | org_credit_quota | Prepaid credits for the managed OpenBricks AI provider are used up. |
403 | not_allowed | The model, provider or tool isn't allowed for this key. | |
400 | pii_detected | The PII guard blocked the request. The message names the categories, never the values. |
Use the error code to decide what to do: back off on rate limits, alert an owner on budgets and quotas, and fix the request on not_allowed or pii_detected.
Data protection
PII guard
Your organisation — or a department — decides what happens to sensitive identifiers before a request reaches a provider:
- off: forwarded unchanged; still masked in logs.
- redact: matches are replaced before the provider sees them, and the response carries
X-Pii-Redactedwith the categories, such asssn,credit_card. - block: the request is refused with
400 pii_detectedand the provider is not called.
The guard is pattern-based: it catches structured identifiers such as social security numbers, card numbers, IBANs and secrets — not names, phone numbers, postal addresses or other free-text personal data. A request with no matches is forwarded byte for byte.
Logging
- Requests are logged after the response is delivered, never on the streaming path.
- Log redaction masks sensitive data before records leave the gateway. Authorization headers, gateway keys and provider keys are never logged.
- Your organisation can store metadata only (no prompt or response bodies) and set how long bodies are kept.
- Reading prompt content requires the right role, and every reveal is recorded in the prompt access log.
Marking retrieved context
For RAG applications, mark which messages contain retrieved documents by adding "rag_message_indices": [2, 3] to X-Gateway-Metadata. Those messages are recorded as injected context rather than as the user's prompt.
MCP Gateway: connecting AI clients
The MCP Gateway gives AI agents governed access to business tools over MCP (Streamable HTTP). Each of your organisation's MCP servers has its own URL:
https://api.platform.openbricks.ai/<server>/mcpThe OpenBricks app catalogue (Gmail, GitHub and the other apps your organisation enables) is the server managed: https://api.platform.openbricks.ai/managed/mcp. Servers your organisation adds itself use their own slug.
Sign in from Claude, Cursor and other MCP clients
Clients that support MCP sign-in need only the URL. In Claude, open Settings → Connectors → Add custom connector and paste it; in Cursor, add it as an MCP server. The client opens the OpenBricks web app, you sign in (password or SSO) and approve the connection on a consent page. No key or header is involved.
- The connection works for MCP only, and only for the server it was approved for.
- It appears under My connections, where you can disconnect it at any time.
- Your access rules, limits and logging apply to it exactly as to your own keys.
Connect with a key
For service accounts, and clients without MCP sign-in, send a gateway key as the bearer token:
{
"mcpServers": {
"openbricks": {
"type": "http",
"url": "https://api.platform.openbricks.ai/managed/mcp",
"headers": { "Authorization": "Bearer YOUR_KEY" }
}
}
}403.Some catalogue apps act in your own account — your Gmail, your GitHub. Connect them once under My connections in the web app before your agent calls their tools.
Access and tool rules
Administrators decide who may call which tools. Rules allow or deny tools on a server — or on every server — for everyone, a role, a team, a service account or a single member. Tools are exact names or prefixes ending in *, such as GMAIL_*.
- Nothing is allowed without a matching allow rule, and a matching deny rule always wins.
tools/listreturns only the tools your key may call. A server you have no rule for answers404, as if it didn't exist.- Destructive tools — ones their server marks as deleting or revoking data — need an explicit rule that allows destructive tools. A tool the gateway hasn't seen listed yet is treated as destructive.
- Tool arguments pass through the same PII guard as prompts: redact masks them, block refuses the call. Tool results are returned unchanged.
What the gateway serves
initialize, ping, notifications, tools/list and tools/call. Other MCP methods, such as resources/* and prompts/*, aren't governed, so they are answered with "method not found". JSON-RPC batches are refused. Your key is never forwarded to the MCP server.
Refusals and limits
A tool call refused by a rule, the destructive-tool check or the PII guard is returned as a JSON-RPC error with HTTP 200, so your client shows it as a failed tool call with the reason. Authentication, plan and limit refusals use HTTP 401, 403, 404 and 429. Rate limits apply as for model requests; budgets don't, since tool calls have no model cost. Every tool call, allowed or refused, is logged with its server and tool name.
Migrating from Portkey
OpenBricks accepts Portkey's request headers and config JSON, so existing Portkey SDK clients and saved configs work without code changes.
- Recreate your provider keys in OpenBricks under Governance → Providers, using the same names as your Portkey virtual keys.
- Point your SDK's base URL at
https://api.platform.openbricks.aiand replace the Portkey API key with an OpenBricks key. - Keep sending
x-portkey-*headers and configs as before, or switch to the neutralX-Gateway-*names over time.
Responses carry only neutral headers (X-Gateway-Trace-Id, X-Gateway-Retry-Attempt-Count); no x-portkey-* response headers are sent.