Skip to content

DocsGet started

Set up with an AI agent

Paste one prompt into Claude Code, Codex, Cursor or another coding agent to route your application's model calls through INFRO, with tests and a report.


INFRO is in private early access. Running these examples requires an early-access account, an active trial or subscription entitlement, and your own provider connection. Self-serve purchasing is not generally open. See Early access.

The integration is a small, repeatable change: find every model call, point the supported ones at INFRO, move model ids into one place and handle INFRO's own errors. A coding agent can do that across a repository in one session. The prompt below tells it exactly how, including what to leave alone.

Choose your agent

Claude Code claude Run it in the repository root and paste the prompt.

View as Markdown
Read the full prompt · 116 lines
# Integrate INFRO into this codebase

You are working in this repository as a senior engineer. Route this application's AI model calls through INFRO with the smallest correct change, verify it, and report what you did. Follow every rule below. Do not guess beyond the facts given here; when something is unclear, read the documentation linked at the end or stop and report.

Prompt version 2026-10-06 · https://infro.io/integrate.md

## What INFRO is

INFRO is an AI gateway and control plane. The team connects AI provider accounts they already hold (OpenAI, Anthropic, Google, Azure OpenAI, AWS Bedrock and others) in the INFRO console. The application then calls one OpenAI-compatible API with an INFRO gateway key. INFRO routes each request to those connections, fails over between them, records every request with its estimated provider cost, and enforces budgets and model policy. Providers keep billing the team directly for model usage.

## Facts to rely on

- Base URL: `https://api.infro.io/v1`
- Authentication: `Authorization: Bearer $INFRO_API_KEY`. Gateway keys start with `sk_infro_`.
- Model ids are `vendor/model` (for example `openai/gpt-5.6-terra`, `anthropic/claude-sonnet-5`, `google/gemini-3.7-flash`), or the slug of a route the team created in the console. `GET https://api.infro.io/v1/models` lists exactly the catalog ids this key can call. For a provider whose ids INFRO cannot derive (LaoZhang, Together, Fireworks, Bedrock, an OpenAI-compatible endpoint and others), `<provider>/<that provider's own model id>` is also valid and is sent exactly as written; those ids are not in `/models`, so take them from the codebase or the provider's own model list.
- Text generation is OpenAI-compatible: `POST /chat/completions` with the OpenAI request and response shape, including streaming, tool calls, structured outputs and image inputs.
- Other endpoints: `POST /images/generations`, `POST /images/edits`, `POST /audio/speech`, `POST /audio/transcriptions`, and `POST /videos`, which returns an async job read with `GET /jobs/{id}` or delivered to a signed webhook.
- TypeSafe's Jev: `POST /systemone` with TypeSafe's own request body and model names (`jev-latest`, `jev-1.13.0`), on the team's TypeSafe connection. Code that uses TypeSafe's SDK needs no code change: set `TYPESAFE_BASE_URL=https://api.infro.io` and `TYPESAFE_API_KEY` to the INFRO gateway key.
- Not available through INFRO: the OpenAI Responses API (`/responses`), embeddings, moderations, Assistants and threads, Realtime, Batch, Files, fine-tuning and vector stores, and provider-native APIs such as Anthropic Messages or Gemini `generateContent` called with their own SDKs.
- Every response carries the header `X-INFRO-Request-Id`. Response bodies add `provider`, `connection`, `route`, `route_name` and `usage.cost`, an estimate in USD of what the provider will bill. `usage.cost` is null when a model is unpriced; that does not mean free.
- Optional top-level body fields: `fallbacks` (up to 5 alternate model ids, tried after every connection for the requested model fails; ignored when `model` is a route slug, whose failover is configured on the route), `metadata` (an object recorded on the request and never sent to the provider; `user_id`, `session_id` and `tags.<key>` can be filtered in the request log; the reserved keys `workflow`, `trace_id`, `step` and `feature` group cost by run and by product feature, and requests without a JSON body can send them as `X-INFRO-Workflow`, `X-INFRO-Trace-Id`, `X-INFRO-Step`, `X-INFRO-Feature` and `X-INFRO-User-Id` headers; `user_id` is also the end-customer id that per-end-customer spend caps count against, so send the application's own opaque user id, never an email address or a name), and `logging: false` (opt this request out of payload capture). To keep one request on particular providers the customer has connected, send `routing: { providers: { allow: ["bedrock"] } }` (or `deny`) with provider ids; leave it out to let the gateway choose.
- Streaming: send `stream_options: { include_usage: true }` to get the cost estimate in the final chunk. Failover happens only before the first byte. A stream that fails midway ends without `data: [DONE]`; treat that as an error, not a complete answer.
- Errors use `{ "error": { "message", "type", "code" } }`. Branch on the HTTP status and `code`, never on the message.
  - 400 `invalid_request_error` — fix the request.
  - 401 `authentication_error` — missing or revoked key.
  - 402 `budget_exceeded` (a budget the team set, an unpriced model under a blocking budget, or codes `subscription_required`, `trial_payment_failed`) — do not retry. Code `end_user_budget` means the end customer in `metadata.user_id` reached its own spend cap: the error also carries `end_user_id`, `period` and `resets_at`, so surface a message the application can show that user ("daily limit reached, resets at …").
  - 400 `end_user_id_required` — the team requires an end-customer id on every request; send `metadata.user_id` (or `X-INFRO-User-Id`). Do not retry without it.
  - 403 `permission_denied`, code `model_not_allowed`, `route_not_allowed`, `project_archived`, `key_disabled` or `ip_not_allowed` — model, key, project or network policy (`key_disabled`: someone turned the key off; it can be enabled again on the Keys screen) (`ip_not_allowed`: the key accepts requests only from its allowed networks); code `guardrail_blocked` or `guardrail_unavailable` — the organization's guardrails refused the request or the response; code `organization_suspended` — INFRO suspended the organization's access, tell the user to contact support@infro.io. Do not retry any of them.
  - 413 `request_too_large` — the body is over the endpoint's limit; do not retry.
  - 404 `model_not_found` — wrong model id.
  - 429 `rate_limit_exceeded` — INFRO's per-key limit, or code `provider_rate_limited` when the team's own provider account is throttling. Retry after `Retry-After`.
  - 408, 502, 503 — retry with exponential backoff and jitter, EXCEPT 503 with code `no_provider_connected`, `connection_out_of_scope` or `model_not_served`, which no retry clears: a provider connection is missing, the existing one is out of reach (another environment, disabled, missing a model map, or temporarily ejected — the message says which), or its key has no access to the model on the provider's side.
- `Idempotency-Key` is honoured on `POST /videos`. Send one per logical job.

## Rules

1. Never print, log, echo, commit or paste the value of `INFRO_API_KEY` or any provider key. Do not read `.env` values into your output. Use placeholders such as `sk_infro_...` in examples.
2. The INFRO key is a server-side secret. Never expose it to browser or mobile code (for example `NEXT_PUBLIC_`, `VITE_`, `EXPO_PUBLIC_` variables, client components, or bundled front-end code). If a model call runs in the browser today, move it behind a server route or function. If the project has no server side, stop and report instead of shipping the key to clients.
3. Make the smallest correct change. Keep prompts, message content, parameters, tools, output parsing and business logic exactly as they are. Do not upgrade SDK major versions unless a base URL override is impossible otherwise, and say so if you do.
4. Follow the repository's existing conventions: language, formatting, config and secrets handling, error and logging style, test framework.
5. Do not add provider API keys for INFRO. Providers are connected in the INFRO console. Keep an existing provider key only where a call stays direct.
6. Do not invent model ids. If you cannot confirm an INFRO id for a model, keep the call direct or leave a clearly marked TODO in one mapping, and list it in your report.
7. If the change needs a large refactor (for example converting many Anthropic-native or Gemini-native calls), do the straightforward call sites, leave the rest direct, and report them with an estimate instead of forcing it.

## Steps

### 1. Inventory

Search the whole repository for model calls, including:
- SDK imports: `openai`, `@anthropic-ai/sdk`, `anthropic`, `@google/genai`, `@google/generative-ai`, `google-genai`, `ai` and `@ai-sdk/*`, `langchain` and `@langchain/*`, `llama_index`, `litellm`, `groq`, `mistralai`, `cohere`, `together`, and OpenAI-compatible clients in Go, Java, .NET, Ruby, PHP or Rust.
- Raw HTTP calls to `api.openai.com`, `api.anthropic.com`, `generativelanguage.googleapis.com`, `openai.azure.com`, `bedrock-runtime`, `openrouter.ai`, and other model hosts.
- Environment variables such as `OPENAI_API_KEY`, `OPENAI_BASE_URL`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, and hard-coded model names.

For each call site record: file, SDK or client, API used (chat completions, responses, embeddings, images, audio, native messages), model, streaming, tools or structured output, and where it runs (server, edge, browser, mobile, background job).

### 2. Classify

- Route through INFRO: OpenAI-compatible chat completions from any language, OpenAI SDK image, speech and transcription calls, and framework adapters that use chat completions.
- Convert, only when straightforward: provider-native text calls (Anthropic Messages, Gemini) that use features the chat completions shape covers (messages, system prompt, streaming, tools, JSON output, images). Use the OpenAI client pointed at INFRO with the matching `vendor/model` id.
- Leave direct and report: every surface listed under "Not available through INFRO", and native calls that depend on provider-only features.

### 3. Implement

- Add one small INFRO client module or factory in the project's style that reads `INFRO_API_KEY` and `INFRO_BASE_URL` (default `https://api.infro.io/v1`). Reuse it at every routed call site instead of constructing clients inline.
- If the project already has a config layer, add a single switch (for example `AI_GATEWAY=infro|direct`) so the change can be reverted without a code change. Skip this if there is no config layer to put it in.
- Replace model ids with INFRO ids in one mapping or in configuration, not scattered through the code.
- Where the call site already knows them, attach `metadata` such as `user_id`, `session_id`, `workflow`, `feature` and `tags` for cost attribution. `feature` is a reserved key of its own (up to 64 characters of letters, digits and `_ . : -`); do not put it under `tags`. Never put secrets or raw personal data in metadata, and do not add new data flows to get it.
- Client specifics:
  - OpenAI Python: `OpenAI(api_key=os.environ["INFRO_API_KEY"], base_url=INFRO_BASE_URL)`. Pass INFRO fields with `extra_body={"fallbacks": [...], "metadata": {...}}`.
  - OpenAI Node or TypeScript: `new OpenAI({ apiKey: process.env.INFRO_API_KEY, baseURL: INFRO_BASE_URL })`. Extra body fields are sent as given; type them with an intersection instead of `any`.
  - Vercel AI SDK: use `createOpenAICompatible` from `@ai-sdk/openai-compatible` with `baseURL` and `apiKey`, or `createOpenAI({ baseURL, apiKey }).chat(modelId)`. Do not use the default `openai(modelId)` call, which targets the Responses API.
  - LangChain: `ChatOpenAI` with the INFRO key and base URL (`base_url` in Python, `configuration.baseURL` in JavaScript). Do not enable the Responses API.
  - LiteLLM: the `openai/` provider prefix with `api_base` set to INFRO, for example `model="openai/anthropic/claude-sonnet-5"`.
  - Any other language: an OpenAI-compatible client with a base URL override, or plain HTTP to the endpoints above.
- Errors and retries: keep the client's existing retry behaviour, and make sure no custom retry loop retries 400, 401, 402, 403, 404, 413, `no_provider_connected`, `connection_out_of_scope` or `model_not_served`. Surface budget and model-policy refusals as distinct, readable errors. Log `X-INFRO-Request-Id` on failures where the client exposes response headers (for example `.withResponse()` in openai-node, `with_raw_response` in openai-python).
- Streaming: request usage in the final chunk where cost is recorded, and handle a stream that ends without `[DONE]` as a failure.
- Configuration and documentation: add `INFRO_API_KEY=` and `INFRO_BASE_URL=https://api.infro.io/v1` to `.env.example` or the project's equivalent, the config schema or validation, deployment manifests and CI secrets lists where the project defines them, and the README's setup section. Remove a provider key from those files only if no call still uses it.

### 4. Test

- Update mocks and fixtures that assert on the old base URL, key variable or model ids.
- Add focused tests for the client factory (key and base URL wiring) and for error classification (which statuses and codes are retried). Do not make live network calls in the test suite.
- Run the project's typecheck, lint and tests, and fix what this change broke.

### 5. Verify with one real request

If `INFRO_API_KEY` is present in your environment, send one minimal request with a model from the inventory and `max_tokens` of 16, without printing the key. Report the HTTP status, the returned `model`, `provider`, `connection` and `usage.cost`, and the `X-INFRO-Request-Id` header. On 503 `no_provider_connected`, tell the user which provider to connect in the console. On 401, tell them to check the key. If the key is not available, give the user this command to run instead:

```bash
curl -sS -D - https://api.infro.io/v1/chat/completions \
  -H "Authorization: Bearer $INFRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "openai/gpt-5.6-terra", "max_tokens": 16, "messages": [{"role": "user", "content": "Reply with OK"}]}'
```

### 6. Report

Finish with:
1. Summary: how many call sites now go through INFRO, and how many stay direct.
2. Inventory table: file, client, API, model before and after, status (routed, converted, direct), reason.
3. Files changed, grouped by purpose.
4. Verification: commands run and their results, including the real request if you made one.
5. Steps for the user in the INFRO console (https://dash.infro.io): connect every provider the routed models need, create a gateway key for each environment, set `INFRO_API_KEY` in each deployment, and optionally add a route, a budget and a fallback.
6. How to roll back: the switch, or the files to revert.
7. Open questions and TODOs.

## Documentation

- Quickstart: https://infro.io/docs/quickstart
- Connect a provider: https://infro.io/docs/providers
- Text generation: https://infro.io/docs/api/chat-completions
- Streaming: https://infro.io/docs/api/streaming
- Errors: https://infro.io/docs/api/errors
- Routes and failover: https://infro.io/docs/routes and https://infro.io/docs/features/fallbacks
- SDKs and frameworks: https://infro.io/docs/sdks
- Full documentation as plain text: https://infro.io/llms-full.txt

Before you paste

  1. Connect a provider you already hold in the console under Providers. Without one, every request answers 503 no_provider_connected. See Connect a provider.
  2. Create a gateway key for the environment the agent will test against, and set it as INFRO_API_KEY in the shell or environment the agent runs in. Never paste a key into the agent's chat: the prompt verifies with the variable and never prints it.
  3. Start from a clean working tree on a new branch, so the agent's change is one reviewable diff.
  4. On a large repository, ask for the inventory first. Most agents have a plan or ask mode; review the call-site table before letting it edit.

What the prompt asks for

StepWhat the agent doesWhat you check
InventoryFinds every model call: SDK imports, raw HTTP to provider hosts, key variables and model names, and where each call runs.Nothing is missing, especially background jobs and scripts.
ClassifyMarks each call as routed, converted from a provider-native API, or left direct, with a reason.Anything left direct is expected.
ImplementAdds one INFRO client module, moves model ids into one mapping or config, attaches metadata where the call site already has it, and updates .env.example and setup docs.Prompts and business logic are unchanged.
Handle errorsMakes sure 400, 401, 402, 403, 404, 413, no_provider_connected, connection_out_of_scope and model_not_served are never retried, and logs X-INFRO-Request-Id on failures.Budget and policy refusals read clearly.
TestUpdates mocks, adds tests for the client and error handling, and runs typecheck, lint and tests without live calls.The suite passes on your machine and in CI.
VerifySends one small request to https://api.infro.io/v1/chat/completions if INFRO_API_KEY is set, and reports the provider, connection, estimated cost and request id.The request appears in the console's request log.
ReportSummarises the change, the inventory, files touched, console steps and how to roll back.The console steps are done before you deploy.

What stays on your provider directly

The prompt tells the agent to leave these calls as they are and list them in its report, because INFRO does not serve them today:

SurfaceWhy it stays direct
EmbeddingsINFRO does not serve embeddings. Vectors from different models are not comparable, so these calls are not moved.
OpenAI Responses API, Assistants, RealtimeINFRO's OpenAI-compatible surface is chat completions. The Vercel AI SDK's default openai(modelId) call targets the Responses API, so the prompt switches it to a chat-completions provider.
Batch, Files, fine-tuning, vector stores, moderationsAccount-level provider features with no INFRO equivalent.
Provider-native featuresAnthropic Messages or Gemini calls that depend on features the chat completions shape does not carry are left direct rather than half-converted.

Review the change

  • INFRO_API_KEY is read only in server-side code. No NEXT_PUBLIC_, VITE_ or EXPO_PUBLIC_ variable carries it, and no client bundle imports the INFRO client.
  • No key value appears in the diff, the report, test fixtures or logs.
  • Every model id is a vendor/model id or a route slug from your console, and they live in one place. GET /v1/models lists what your key can call.
  • Streaming call sites treat a stream that ends without data: [DONE] as a failure.
  • metadata carries identifiers you already use, such as a user or session id, and no secrets or raw personal data.
  • The rollback is a config switch or a small set of files, and the report says which.

Operate INFRO from your assistant

This page sets up your application's model calls. To let the same assistant read your INFRO organization (spend, failing requests, traces, budgets, configuration) and make small changes with your confirmation, connect the INFRO MCP server.

Keep the rules in the repository

After the change lands, add a short rules section so later agent sessions keep calling models the same way. Most coding agents read AGENTS.md at the repository root; some use their own file, such as CLAUDE.md for Claude Code, GEMINI.md for Gemini CLI and .github/copilot-instructions.md for GitHub Copilot. Fill in the placeholders.

AGENTS.md
## AI model calls (INFRO)

- All model calls go through INFRO at `https://api.infro.io/v1` using the shared client in <path to the INFRO client module>.
- Authenticate with `INFRO_API_KEY`, server-side only. Never expose it to browser or mobile code, and never log or commit it.
- Use INFRO model ids (`vendor/model`) or route slugs, kept in <path to the model mapping or config>. `GET https://api.infro.io/v1/models` lists what the key can call.
- Attach `metadata` (`user_id`, `session_id`, `workflow`, `feature`) where the call site knows them.
- Do not retry 402, 403, 413, or 503 with code `no_provider_connected`, `connection_out_of_scope` or `model_not_served`.
- Embeddings, the Responses API and other surfaces INFRO does not serve stay on the provider directly: <list them>.
- Reference: https://infro.io/docs/agents

Where to paste it

The prompt is plain text, so any agent that can read and edit your repository can follow it. Agents with web access can also fetch it from infro.io/integrate.md. Logos and names identify tools only; no partnership or endorsement is implied.

AgentTypeHow to start
Claude Code · AnthropicCLIRun claude. Run it in the repository root and paste the prompt.
Codex · OpenAICLIRun codex. Run it in the repository root, or start a Codex task on the repository, and paste the prompt.
Cursor · AnysphereIDEOpen the repository, start an Agent chat and paste the prompt.
GitHub Copilot · GitHubIDEOpen Copilot Chat in agent mode in your editor and paste the prompt.
Windsurf · WindsurfIDEOpen the repository, start a Cascade chat and paste the prompt.
Gemini CLI · GoogleCLIRun gemini. Run it in the repository root and paste the prompt.
Cline · ClineExtensionOpen Cline in your editor, review the plan it proposes, then let it act.
Kiro · AWSIDEOpen the repository, start an agent chat and paste the prompt.
Junie · JetBrainsIDEOpen Junie in your JetBrains IDE and paste the prompt as a task.
Amp · AmpCLIRun amp. Run it in the repository root, or use its editor extension, and paste the prompt.
Roo Code · Roo CodeExtensionOpen Roo Code in your editor in a coding mode and paste the prompt.
Kilo Code · Kilo CodeExtensionOpen Kilo Code in your editor in a coding mode and paste the prompt.
OpenCode · SSTCLIRun opencode. Run it in the repository root and paste the prompt.
Trae · ByteDanceIDEOpen the repository, start an agent chat and paste the prompt.
Devin · CognitionCloudStart a session on the repository and paste the prompt as the task.
Replit Agent · ReplitCloudOpen your app in Replit, start Agent and paste the prompt.
Lovable · LovableApp builderPaste the prompt into the project chat. The key stays in a server-side function.
v0 · VercelApp builderPaste the prompt into the project chat. The key stays in a server route.

If the verification request fails

ResponseMeaningFix
401 authentication_errorThe key is missing, mistyped or revoked.Check INFRO_API_KEY in the agent's environment.
503 no_provider_connectedNo connection serves this model's vendor.Connect that provider in the console, then retry once.
503 connection_out_of_scopeA connection exists but is out of reach: another environment, disabled, missing a model map, or temporarily ejected. The message says which.Fix what the message names in the console, or use a key for the connection's environment.
503 model_not_servedEvery connection tried answered that its key cannot serve this model (a provider group or project without it). The message names each one.Stop and tell the user which key needs access on the provider's side, or pick another model. Do not retry.
403 model_not_allowedA model allowlist on the organization, project or key refuses it.Pick an allowed model or change the policy.
403 project_archivedThe key belongs to a project that was archived.Restore the project in the console, or use a key from an active project.
403 key_disabledA member turned this gateway key off.Stop and tell the user: the key can be enabled again on the Keys screen. Do not retry.
403 organization_suspendedINFRO has suspended this organization's access.Stop and tell the user to contact support@infro.io. Do not retry.
404 model_not_foundThe model id is not an INFRO id or route slug.Use an id from GET /v1/models.
402 budget_exceededA budget you set has no room for the request.Raise the budget or wait for its period to reset.
402 subscription_requiredThe organization has no active trial or plan.Start a trial or plan in the console.
429 provider_rate_limitedYour own provider account is rate-limiting you.Wait for Retry-After, or add a connection with more headroom.

Next steps

  • Quickstart — the same integration by hand.
  • Routes — choose connection order and failover without changing code.
  • Errors — every status and code, and which to retry.
  • Request traces — find the agent's verification request in the console.
↑ ↓ navigate↵ open/ anywhere in docs