Put Gate in front of your AI model API key
Your agents call a model API with one provider key. Put Gate in between: each agent gets its own Gate key, only the calls you allow pass, a stuck agent repeating the same request is stopped before it reaches the provider, and your provider key never leaves Gate.
Set it up
- Create a project whose upstream is your provider's API base URL and whose secret header is your provider key (for an OpenAI-compatible API,
Authorization: Bearer <key>; for Anthropic,"header_name": "x-api-key"). The key is stored encrypted (AES-GCM). You get the project id and an admin key once. - Add one app per agent with only the calls it needs, its calls per minute and, if you like, the only models it may ask for. You get that agent's Gate key once.
- Point the agent's SDK base URL at
https://gate.odilelabs.com/g/<project id>and give it its own Gate key instead of the provider key. Streaming answers pass straight through.
curl -sS https://gate.odilelabs.com/api/projects -H 'content-type: application/json' \
-d '{"name":"Model API","upstream":"https://api.openai.com/v1","header_value":"Bearer '"$PROVIDER_KEY"'"}'
curl -sS -X POST "https://gate.odilelabs.com/api/projects/$PROJECT/apps" \
-H "Authorization: Bearer $ODILE_GATE_ADMIN_KEY" -H 'content-type: application/json' \
-d '{"name":"support-agent","routes":["POST /chat/completions"],"per_minute":60,"models":["gpt-4.1-mini"]}'
# Python, OpenAI SDK: the agent holds only its Gate key from openai import OpenAI client = OpenAI(base_url="https://gate.odilelabs.com/g/" + PROJECT, api_key=GATE_APP_KEY) # Python, Anthropic SDK (project header_name x-api-key): send the Gate key as a bearer token from anthropic import Anthropic client = Anthropic(base_url="https://gate.odilelabs.com/g/" + PROJECT, auth_token=GATE_APP_KEY)
What it stops
- A looping agent: the same request body sent again and again (by default 10 times inside 10 seconds, or 5 at a machine-steady pace) gets a 429 with
x-odile-gate-loop-detected: truebefore the provider bills it. - Runaway volume: each agent has its own per-minute limit, and the project has its plan's monthly call allowance. Limits count calls, not tokens or dollars.
- Calls you never meant to allow: anything outside the agent's list is refused, and the refusal reason is in the
x-gate-refusedheader. - A model you did not allow: an agent limited to some models (exact names or a prefix such as
claude-haiku-*) that asks for another gets a 403 withx-gate-refused: model_not_allowedand the allowed list, before the provider sees the call. Change the list withPUT /api/projects/<id>/apps/<app>/modelsor thegate_set_modelsMCP tool. - A leaked agent key: revoke or rotate that one agent's Gate key; the provider key and the other agents keep working.
Rotate the provider key
Replace the stored key in place, or remove it, with the project admin key. The value is never returned or logged.
curl -sS -X PUT "https://gate.odilelabs.com/api/projects/$PROJECT/secret" \
-H "Authorization: Bearer $ODILE_GATE_ADMIN_KEY" -H 'content-type: application/json' \
-d '{"header_value":"Bearer '"$NEW_PROVIDER_KEY"'"}'
What Gate keeps
Gate never stores prompts or answers: request and response bodies, headers and query values are not kept. The audit log has the time, agent, allowed or refused call, status, duration, body size and a hash.
Plans
Free: 3 apps and 10,000 calls a month. Pro: $29 a month, 25 apps and 1,000,000 calls a month. Your model usage is billed by your provider as before.