Documentation/Quickstart and API reference

Build with router

Use OpenAI-compatible APIs, Anthropic-compatible Messages, or your favorite AI app with one model catalog.

Quick start#

Create an account, fund its wallet and create an API key. Use the deployment origin plus /v1 for OpenAI clients, and the origin without /v1 for Anthropic clients. Send Authorization: Bearer YOUR_API_KEY or X-Api-Key, and keep the secret on your server. New keys default to inference and usage:read; account management needs separate permissions.

  1. Create an account and open your workspace.
  2. Make sure your wallet has enough balance for the request.
  3. Create an API key. Copy the full secret once and store it securely.
  4. Set your SDK's base URL to router and use an exact model ID from the catalog.
  5. Send a request, then review its usage and settled cost in your workspace.
Using an AI app?
Follow the Codex, Claude Code, or Cursor guide to keep working in the tools you already use.

Base URLs

OpenAI-compatible base URL
https://your-router-domain/v1
Anthropic-compatible base URL
https://your-router-domain

Anthropic SDKs append /v1/messages themselves. Use the base URL without that path.

For the shell examples below, set ROUTER_BASE_URL to this site's origin without /v1. Use ROUTER_API_KEYfor inference and ROUTER_MANAGEMENT_KEY for a key with the required account permissions.

Authentication

Send Authorization: Bearer YOUR_API_KEY. The Messages endpoint also accepts X-Api-Key. Keep keys on your server or in local environment variables.

API key permissions

New keys default to inference and usage:read. Select additional permissions when creating a key in the workspace. A valid key without the permission required by an endpoint returns 403.

FieldDescription
inferenceChat Completions, Responses, Messages, model/supply reads, and POST /v1/feedback.
usage:readGET /v1/usage, /v1/usage/requests, /v1/usage/daily, /v1/models/compare and /v1/pricing/changes.
account:readGET /v1/account, /v1/account/balance, /v1/account/usage, /v1/account/savings, /v1/keys, /v1/techniques and /v1/experiments, including experiment detail.
account:writeCreate/revoke keys through /v1/keys and PATCH /v1/techniques/{id}. Policy editing requires a browser session.

Legacy text completions

POST /v1/completions accepts one string prompt or an array containing one string and returns choices[].text. Streaming and echo are supported. It uses the same routing and settlement as Chat Completions. Batched or token-ID prompts, nonempty suffix insertion, logprobs, best_of or n other than 1, and tools are unsupported.

Chat completion

One language setting updates the request examples on this page.

chat/completionscURL
curl 'https://your-router-domain/v1/chat/completions' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "model": "deepseek-v4-flash",
  "messages": [
    {
      "role": "user",
      "content": "Hello!"
    }
  ],
  "max_tokens": 128
}'

Give each key a clear boundary#

Create and edit API key policies in the signed-in workspace. Empty model and IP lists mean unrestricted; null numeric limits or expiry remove those limits. Model allowlists, client IP/CIDR, expiry, UTC-minute rate, UTC-day quota, concurrency and monthly spending limits are enforced by the gateway. List, update and revoke return only the key prefix; the full secret is shown once at creation. Account and balance reads require account:read. API keys default to prompt_cache_mode passthrough; on/off/passthrough are editable advanced settings. x-ci-prompt-cache overrides the key. For Claude models, on needs a compatible native Messages route (currently claude-opus-4.8 only), and off keeps client cache_control while disabling affinity. x-ci-prompt-cache-scope selects session/user/org; the session label is printable ASCII of 1–256 characters. The x-ci-prompt-cache-affinity new/hit/stale/miss header is a five-minute route preference, not proof of supplier cache reads or guaranteed savings.

Open API keys to create a key or edit its policy. Advanced limits are optional and enforced by the gateway. Empty model/IP lists allow every model/source. Blank numeric limits and expiry mean unlimited/no expiry.

FieldTypeDescription
allowed_modelsstring[]Up to 100 model IDs or aliases. [] means unrestricted; null is invalid.
allowed_ip_cidrsstring[]Up to 50 IPv4 or IPv6 addresses/CIDRs. [] allows any source IP. Restricted keys fail with 403 when a trusted Cloudflare client IP is unavailable.
expires_atISO timestamp | nullFuture expiry as an absolute timestamp. null removes expiry. The dashboard displays local time.
rate_limit_per_minuteinteger | null1–100,000 accepted logical inference requests per fixed UTC minute.
daily_request_limitinteger | null1–1,000,000,000 accepted logical inference requests per UTC day.
concurrency_limitinteger | null1–10,000 in-flight logical inference requests.
monthly_spend_limit_usdnumber | null$0–$1,000,000, with up to 9 decimal places. null is unlimited; $0 blocks positive-cost inference.

Every accepted logical inference counts once, including cache hits and failed requests. Both the requested model and the final experiment/technique model must satisfy the allowlist. Budget checks include in-flight reservations and can reject a request whose estimated maximum charge exceeds the remaining allowance. Monthly spending belongs to the UTC month when the request is accepted.

Rate, daily, concurrency and key monthly-budget limits return 429; insufficient wallet balance returns 402. Expired or revoked keys return 401. Scope, model, IP and management permission failures return 403.

Policy edits constrain new admissions. Already-reserved requests can complete under their original authorization. A policy change between authentication and reservation rejects the request with 403; retry under the updated policy.

Read account and balance

These endpoints require account:read and return current account data and wallet amounts in USD. Available balance excludes in-flight reservations. Active key count excludes revoked and expired keys. A workspace key uses its fixed organization_id; a signed-in session selects a workspace with the same query parameter. Omit it for personal account data. Workspace balances show the owner-funded shared wallet, while email and platform role identify the responsible member. Owner card and automatic-recharge settings stay private; workspace reads return false or null for those settings.

accountcURL
curl 'https://your-router-domain/v1/account' \
  -H 'Authorization: Bearer YOUR_API_KEY'
account/balancecURL
curl 'https://your-router-domain/v1/account/balance' \
  -H 'Authorization: Bearer YOUR_API_KEY'

List and create keys through the API

keyscURL
curl 'https://your-router-domain/v1/keys' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Create a scoped keyShell
curl "$ROUTER_BASE_URL/v1/keys" \
  -H "Authorization: Bearer $ROUTER_MANAGEMENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Application inference","scopes":["inference","usage:read"],"allowed_models":["deepseek-v4-flash"],"rate_limit_per_minute":60,"monthly_spend_limit_usd":25}'
A management key cannot grant more access than it has
Listing keys requires account:read; creation and revocation require account:write. API-key callers may grant only their own scope subset and cannot mint additional keys while subject to policy restrictions. Edit policies in a signed-in session. Creating or revoking workspace keys also requires a session: organization_id selects the workspace, and optional member_user_id assigns the responsible member. Members may create keys only for themselves; owners may assign an active teammate. An existing key cannot change workspace or responsible member.

Workspace requests spend the owner's shared wallet. Each member's UTC monthly allowance includes reservations across all their workspace keys, in addition to each key's own limits. New invitations default to $0; the owner must grant a spending allowance. Removal or a tighter limit blocks new admissions; requests already reserved can finish under their original authorization. Usage and account reads stay within the selected workspace; personal history, saved-card settings, experiments, uploads and techniques are not inherited.

The full secret appears only in the creation response. List, update, and revoke responses contain its prefix. Keep the secret on your server; neither the full key nor its hash can be read back.

API reference#

The request builder runs Chat Completions, Responses or Messages requests with your own API key. Its cURL, Python and JavaScript examples share the selected parameters. Running inference can spend wallet balance. GET /v1/docs lists these section overviews; add q to search their titles, keywords and text. A valid API key is required, with no additional scope.

The request builder uses the same public endpoints as your application. Your secret stays in this page's memory; running a request can spend wallet balance.

Search these documentation overviews

GET /v1/docs returns the section directory without bodies. Add q to return matching overview paragraphs, best match first. limit is 1–10, defaults to 4, and is ignored without a nonblank query. Supply q once, with at most 500 characters. The linked sections contain the detailed field tables and interactive examples. This endpoint needs a valid Bearer or X-Api-Key credential, with no additional scope, and does not call a model or spend wallet balance.

docs?q=api%20keys&limit=4cURL
curl 'https://your-router-domain/v1/docs?q=api%20keys&limit=4' \
  -H 'Authorization: Bearer YOUR_API_KEY'
POST
Output monitor

Live response

>_

Make a request

Use an API key to run this example. Requests use your wallet balance.

Manage API keys
Kept in this page only. Never stored in your browser.
requestcURL
curl 'https://your-router-domain/v1/chat/completions' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "model": "claude-fable-5",
  "messages": [
    {
      "role": "user",
      "content": "A farmer has 17 sheep. All but 9 run away. How many are left? Show your reasoning."
    }
  ],
  "max_tokens": 128,
  "temperature": 0.7,
  "stream": false,
  "ranking": "balance"
}'

See the full endpoint reference for request fields, response formats and error codes.

Models and pricing#

GET /public/models and /public/models/{model_id} need no API key. The list returns a models array; detail accepts exact case-sensitive IDs and listed aliases, including slashes. These compatibility endpoints use 0.00 for an unknown discount, while null reference prices retain the unknown distinction; the UI /api/public/models endpoint keeps discount null. Use only visible, price-eligible models; current capacity is checked at admission. Token prices are USD per million input, output, cache-read or cache-write tokens. GET /v1/models/compare estimates a workload without inference. GET /v1/pricing/changes uses an inclusive since timestamp and a version-frozen next_cursor; both reads require usage:read. Catalog observations are not continuous supplier price checks.

Use the exact model ID in the model field. The live catalog lists currently configured models and their prices. A single model can have several underlying routes; router automatically chooses the serving route.

modelscURL
curl 'https://your-router-domain/v1/models' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Use GET /v1/models/compare to estimate a token workload across one to ten models without calling an upstream. Pass comma-separated model IDs, prompt_tokens, completion_tokens and requests. The result includes customer cost estimates and anonymous supplier rates. Actual route and usage determine the bill; unknown list prices and savings remain null.

GET /v1/pricing/changes?since=2026-09-06T00%3A00%3A00Z returns the latest change per allowed model since an inclusive timestamp. Follow next_cursor with the same since and credential context to keep the first page’s catalog version. Both endpoints require usage:read. They describe the configured catalog: pricing_checked_at is null, and pricing_observed_at identifies the observation time. See the price change contract for fields and pagination.

Token rates are in US dollars per million tokens. Input, output, cache reads and cache writes have separate rates. Catalog prices describe current supply; the final charge follows the route that successfully serves your request. When a verified model-maker reference exists, the default ceiling is the list price. Without one, ordinary requests can use configured retail rates and discount/list-price savings remain unknown. Explicit min_discount_percent, including 0, still requires a verified reference and otherwise returns 400 list_price_unknown.

A price you can check
The response includes the persisted customer charge. A minimum-discount requirement needs a verified model-maker reference and limits input and output rates separately. Unknown reference prices are not shown as a 0% discount.
FieldDescription
idExact public model identifier.
aliasesAlternative vendor-qualified identifiers accepted for the same model.
pricing.input_per_million / pricing.output_per_millionCurrent input and output catalog rates in USD.
pricing.cache_read_input_per_million / pricing.cache_write_input_per_millionSeparate input-cache rates.
context_length / max_output_tokensContext capacity and maximum generation length.
capabilities.streaming / capabilities.reasoningCapabilities of the published model.

Response feedback#

POST /v1/feedback accepts your own successful request_id, an integer score from 1 to 5 and an optional comment. Find the ID in the x-ci-request-id response header. Resubmitting replaces the previous score and comment. Feedback does not require an experiment, and does not imply automatic quality judging or statistical confidence.

Attach a score to a completed request using its x-ci-request-id response header. Sign in with the account that made the request to save feedback.

feedbackcURL
curl 'https://your-router-domain/v1/feedback' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "request_id": "",
  "score": 5,
  "comment": "Accurate answer"
}'

Scores are integers from 1 to 5 and accept only your own successful requests. Resubmitting replaces the previous score and comment. Review settled spend, request status, tokens, and feedback together. Average cost divides total spend by all recorded requests; no samples means no average. Savings and statistical confidence are not inferred.

Parameters#

POST /v1/completions accepts a single string prompt (or one-string array), with streaming and echo, and returns choices[].text. Chat Completions uses messages with roles and content. Set max_tokens or max_completion_tokens to bound output, and stream for server-sent events. Tools, tool_choice, response_format, sampling and reasoning options depend on the chosen model and route. Router normalizes supported protocol differences; it cannot add a capability the serving model does not have.

Chat requests use the OpenAI Chat Completions shape. Supported parameters depend on the exact model and serving route. Router handles known format differences before sending your request.

FieldTypeDescription
messagesarrayConversation messages with role and content.
max_tokens / max_completion_tokensintegerAn explicit output-token budget. Set one to bound generation.
temperature / top_pnumberSampling settings, when supported by the model.
tools / tool_choicearray / objectClient-side tool definitions and tool selection.
response_formatobjectJSON object or schema request for models with matching support.
reasoning / reasoning_effortobject / stringReasoning configuration mapped to the serving model.
streambooleanReceive protocol-specific server-sent events.

A compatibility layer cannot add an ability the model does not have. Validate the request shape you use in production, especially tools combined with reasoning or structured output.

Direct image input

Models with capabilities.vision: true in GET /v1/models can accept images in ordinary user messages. The model detail page marks each anonymous route as Text + images or Text only. Image requests use compatible routes at their displayed prices; the gateway never falls back to a cheaper text-only route.

Chat uses image_url content parts; Responses uses input_image; Messages uses image with a URL or base64 source. Supply an HTTP(S) URL without credentials, or canonical base64 for PNG, JPEG, WebP or GIF. The complete JSON request, including base64, must fit within 2 MiB. File IDs and temporary uploads are not provided. See the image input shapes and limits.

Image routing and billing
Image requests skip optional text techniques, exact response caching and automatic model changes. An experiment that modifies the request or explicitly enables techniques returns 400 incompatible_image_input; unchanged experiment attribution remains supported. Reservation uses a conservative image input bound, while final billing uses actual supplier usage. An estimated input bound above 272,000 returns 400 image_input_limit.

Price ceiling#

min_discount_percent is a number from 0 through 99.99. It constrains input and output rates separately against the published model-maker reference price. A request with a minimum discount fails when no eligible route meets both ceilings. GET /v1/models/supply reports candidate counts at current prices; it does not guarantee capacity, health or a successful response.

Add min_discount_percent to set the minimum discount your request accepts. It is a number from 0 to 99.99, checked against router's published model-maker reference price.

chat/completionscURL
curl 'https://your-router-domain/v1/chat/completions' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "model": "deepseek-v4-flash",
  "messages": [
    {
      "role": "user",
      "content": "Hello!"
    }
  ],
  "min_discount_percent": 40,
  "max_tokens": 128
}'

A route must meet the ceiling on both input and output prices. Router removes this field before forwarding the request. If no route qualifies, the request fails instead of silently selecting a more expensive route.

FieldDescription
400 · invalid_min_discountThe value is not a number from 0 through 99.99.
400 · list_price_unknownNo verified comparison price is available for this model.
503 · min_discount_unavailableNo current route satisfies both price ceilings. Lower the required discount or try again later.

Check available supply

models/supply?model=deepseek-v4-flash&min_discount_percent=40cURL
curl 'https://your-router-domain/v1/models/supply?model=deepseek-v4-flash&min_discount_percent=40' \
  -H 'Authorization: Bearer YOUR_API_KEY'

The candidate count describes routes priced below the ceiling at that moment. It does not guarantee health, capacity or a successful response.

Zero data retention#

Enable account zero data retention in /dashboard/data-protection, or send zdr: true for one request. Request false does not lift the account restriction. No verified ZDR supply is configured, so effective ZDR returns 503 zdr_capacity_unavailable. Enabling clears response cache and blocks new uploads and image reads, scheduling existing uploads for deletion; Prompt Studio runs are cancelled and their saved prompts, answers and results are cleared. Billing records, separate experiment configurations and pre-existing browser drafts remain. With the restriction off, POST /v1/uploads stores one PNG/JPEG/WebP/GIF up to 10 MiB for one hour; earlier uploads retain their recorded expiry. Uploading requires inference scope or a same-origin session. Reference the returned id using Responses input_image.file_id, Messages image.source.file_id, or the Router-specific Chat image_url.url router-upload:// prefix. Quotas are 32 stored files/64 MiB per account, including pending cleanup, and rolling limits of 20 admitted uploads per minute/120 per day. See /api-reference#uploads for the exact fields and deletion limits. Normal availability does not prove supplier zero retention.

The zdr: true field restricts a request to verified zero-retention supply. Routes must have verified coverage before they can serve a request that requires ZDR.

Availability
Zero-retention supply has not been verified for the current direct-provider catalog. Requests requiring ZDR return 503 zdr_capacity_unavailable. A model's normal availability is not a zero-retention promise.

Speed, discount, balance#

ranking chooses the order of eligible routes for the same model. balance is the default and combines recent speed and discount; discount starts with the lowest estimated cost; speed prefers recent speed with cost as a tie-break. Routes with too little history receive a neutral score. Ranking does not override price ceilings, privacy requirements or model capabilities.

Use ranking to choose how eligible routes are ordered. The model stays the same; you do not need to select or configure an underlying provider.

FieldDescription
balanceThe default. Combines recent speed and discount information.
discountStart with the lowest estimated cost among eligible routes.
speedPrefer recent speed; cost breaks ties. Routes with too little history receive a neutral score.
chat/completionscURL
curl 'https://your-router-domain/v1/chat/completions' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "model": "deepseek-v4-flash",
  "messages": [
    {
      "role": "user",
      "content": "Hello!"
    }
  ],
  "ranking": "speed",
  "max_tokens": 128
}'

Ranking does not override a price ceiling, a privacy requirement, or a model's capabilities. The route that serves the request determines its settled rate.

Responses API#

POST /v1/responses supports Responses-compatible clients. Set store: false and include the required conversation input with each request. Streaming, function calls and supported local tools are available. Stored responses, previous_response_id, conversations, background mode and provider-hosted web or file search are not supported.

Use POST /v1/responses for Responses-compatible clients such as Codex. Set store: false and include the conversation input needed for each request.

responsescURL
curl 'https://your-router-domain/v1/responses' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "model": "deepseek-v4-flash",
  "input": "Hello!",
  "store": false,
  "stream": true,
  "max_output_tokens": 128
}'
Stateless compatibility
Streaming, function calls and supported local tools are available. Stored responses, previous_response_id, conversations, background mode, and provider-hosted web or file search are not part of this endpoint.

Open the Codex setup guide →

Anthropic Messages#

POST /v1/messages accepts Anthropic Messages and returns the corresponding response shape. Claude Code executes client-side tools locally and sends tool_result blocks back. Streams use message_start, content_block_delta, message_delta and message_stop. Anthropic-hosted tools, Files and Message Batches are not compatibility targets. Receiving opaque native output does not prove that signed or encrypted history can be replayed. POST /v1/messages/count_tokens returns a free, local approximate input_tokens count; x-ci-token-count-estimated is true and x-ci-token-count-method is unicode-heuristic-v1. It requires inference permission and respects the model allowlist; it is not a supplier tokenizer or a context-fit guarantee.

POST /v1/messages accepts the Anthropic Messages format and returns the corresponding response shape. Claude Code executes client-side tools locally and sends their results back to the endpoint.

messagescURL
curl 'https://your-router-domain/v1/messages' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "model": "deepseek-v4-flash",
  "max_tokens": 1024,
  "messages": [
    {
      "role": "user",
      "content": "Hello!"
    }
  ]
}'

Streams use Anthropic event names including message_start, content_block_delta, message_delta and message_stop.

Anthropic-hosted tools, Files and Message Batches are not compatibility targets. Signed, encrypted, and opaque history replay is not supported in this version, including on native routes. The API may preserve these items in native output, but sending them back as history returns 400.

Estimate input tokens

POST /v1/messages/count_tokens accepts model, system, messages and function tools and returns input_tokens without calling a provider, reserving funds or charging the wallet. It requires inference permission and the model allowlist still applies. This local Unicode heuristic is approximate, not the supplier tokenizer or a guaranteed context-fit bound. The response marks x-ci-token-count-estimated: true and x-ci-token-count-method: unicode-heuristic-v1. Media, signed thinking and server tools are unsupported.

Open the Claude Code setup guide →

Errors and limits#

OpenAI-compatible endpoints return an error object; Messages uses the Anthropic error envelope. 400 indicates an invalid or unsupported request; 401 an invalid, expired or revoked key; 402 insufficient balance; 403 a permission or policy denial; 404 a missing resource; 429 a rate or budget limit. Supplier failure, unavailable eligible capacity and timeouts can return 502, 503 or 504. Use status and error code for program logic.

FieldDescription
400Invalid request body or unsupported parameter combination.
401Missing, invalid, expired or revoked API key.
402Insufficient wallet balance.
403The API key lacks a required permission, or the request origin or account access is not authorized.
404The model or requested record was not found.
429A request, concurrency, quota, or key monthly budget limit was reached.
502Eligible upstream requests failed.
503No route meets the required capacity, discount or privacy conditions.
504The serving request timed out.

OpenAI-compatible endpoints use an error object. Messages returns Anthropic's error envelope. Use the status and error code for program logic, and the message for diagnosis.

error responseJSON
{
  "error": {
    "message": "Invalid API key.",
    "type": "authentication_error",
    "code": "invalid_api_key"
  }
}

Reliability#

Each eligible route gets at most two physical attempts: the initial attempt and one retry for qualifying transport failures before downstream output or HTTP 404/408/409/425/429/5xx. A rejected underlying JSON-body read or SSE prebuffer read, or supplier timeout during those reads, can retry after response headers arrive. Attempts share one reservation and the original 180-second deadline. A valid Retry-After is honored in full only if it fits that attempt’s remaining budget; otherwise that retry is skipped. Parsing, decoding, invalid stream frames, usage-validation and database failures are not same-route transport retries. No retry occurs after a streaming response is returned, settlement begins or the client cancels. Only a valid final success is charged once. A new generation cannot continue a partial response. If the client retries a failed stream, start a complete new request and use bounded backoff.

Automatic fallback

Router can retry eligible provider failures and move to another route before output begins. The public model identifier and protocol stay the same.

Streaming boundaries

Once output reaches your client, a partial response cannot be replaced transparently. If a stream fails, retry the complete request. Do not append output from a new generation to a failed one.

Client retry policy

Use bounded exponential backoff with jitter for transient errors. Respect Retry-After when it is present, and set a reasonable timeout and output budget.

Usage and billing#

Successful requests report token usage and the settled customer charge in cheaper_inference.billing and usage.cost. Failed or unsettled requests have no settled amount. Request and daily reports require usage:read and accept start_at inclusive, end_at exclusive and api_key_id. Preserve filters while paging with next_cursor. Daily reporting uses timezone_offset_minutes east of UTC, defaults to 30 days and accepts up to 366 days. Account usage and savings require account:read; old requests without a recorded list-price baseline remain unknown rather than being repriced today. For settled supplier requests, a positive cache-read count is hit, an explicitly reported zero is miss, and absent read reporting is unknown. Historical zero alone is not evidence of a miss. Cache-write counts are independent. Platform exact-cache hits are not_applicable to supplier cache reporting; unsettled requests have a null state. Daily cache percentages include only settled supplier requests with proven cache-read reporting, including explicit zero reads.

Successful requests report token usage and the exact settled customer charge. Internally attempted provider requests do not appear as additional customer bills.

settlement metadataJSON
{
  "usage": {
    "prompt_tokens": 1234,
    "completion_tokens": 321,
    "total_tokens": 1555,
    "cost": 0.012345
  },
  "cheaper_inference": {
    "request_id": "request-id",
    "billing": {
      "status": "settled",
      "billed_cost_usd": "0.012345",
      "currency": "USD"
    }
  }
}

The cheaper_inference namespace is retained for response compatibility. Its billed amount is your charge, not the private cost paid to a supplier. Failed or unsettled requests do not include a settled amount.

Chat streams deliver settlement metadata in a final empty-choice event. Responses includes it in response.completed or a final response.incomplete; Messages includes it in the final message_delta. The x-ci-request-id header helps locate the request.

Usage reports

Both reports require usage:read. Filter by start_at (inclusive), end_at (exclusive) and api_key_id. Timestamps without an offset are interpreted as UTC. Request history uses next_cursor for pagination; keep the same filters while paging. Daily reports fill missing calendar days and count only settled ledger amounts as spending.

usage/requests?limit=25cURL
curl 'https://your-router-domain/v1/usage/requests?limit=25' \
  -H 'Authorization: Bearer YOUR_API_KEY'
usage/daily?timezone_offset_minutes=480cURL
curl 'https://your-router-domain/v1/usage/daily?timezone_offset_minutes=480' \
  -H 'Authorization: Bearer YOUR_API_KEY'

timezone_offset_minutes sets a fixed calendar offset in minutes east of UTC. The default is 0; 480 means UTC+8. Daily reports default to 30 calendar days and accept windows up to 366 days. Unknown cache reporting is not presented as a measured zero hit rate.

Report channels#

Choose Email, Slack, Discord or Telegram in Reports for personal usage digests, excluding Router workspace requests. Email uses the account’s verified address; Slack and Discord use explicit platform authorization, while Telegram requires starting the Bot and confirming the recipient in the same Router session. Connecting leaves the schedule Off and sends no report. Daily, weekly and monthly summaries cover previous complete UTC periods, with scheduled work checked every five minutes. Read /v1/channels with account:read; update report_frequency or explicitly send-report with account:write. Browser mutations require the same origin. Off disables scheduled digests but manual reports can summarize yesterday. Details lists nine optional product events from available_events. Explicitly save events independently of report_frequency; [] unsubscribes, and Off disables only scheduled digests. Existing and reconnected channels are not automatically opted in. Required account, security and billing messages are separate. Missing configuration is a 503 error; sent:false means a successful no-activity result, not failed delivery. Provider acceptance does not establish recipient reading. No automatic resend follows an unknown acknowledgement. Connection management requires the signed-in browser session. See /api-reference#channels.

Reports include personal usage and settled spending without prompt or response bodies or Router team activity. Choose Email, Slack, Discord or Telegram in Reports and explicitly connect the destination before scheduling. Platform acceptance does not establish that a person read it.

Channel operations and connection scope →

Choose optional techniques#

Eight optional techniques default off and only apply to eligible requests. Read settings with GET /v1/techniques using account:read; change enabled with PATCH /v1/techniques/{id} using account:write. Exact-cache stores eligible response text per user for a 15-minute reuse window, not prompt text. Eligible hits have zero usage and charge. Account disable clears current cache; an active experiment explicitly enabling caching can store new entries. No unmeasured percentage savings is claimed.

All eight optional techniques default off. Read their eligibility and quality tradeoffs in workspace controls, or use the API to update your account. A technique only runs when the request meets its conditions.

techniquescURL
curl 'https://your-router-domain/v1/techniques' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Enable an account techniqueShell
curl "$ROUTER_BASE_URL/v1/techniques/concise-output" \
  -X PATCH \
  -H "Authorization: Bearer $ROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true}'
Account controls and experiment overrides
An experiment’s explicit technique selection overrides account settings; null inherits them and an empty array disables all optional techniques. Exact caching stores eligible response text per user with a 15-minute reuse window, up to 100 entries of 256 KiB each. Prompt text is not stored. Disabling the account setting clears existing entries; an active experiment explicitly enabling caching can create new entries. Expired entries are cleaned during writes.

Eligible exact-cache hits return zero usage and zero inference charge. Caching requires temperature 0, non-streaming plain text, and no tools. Model routing can change a simple request to a configured cheaper sibling; it does not promise equivalent quality. No percentage savings is claimed without measured control data.

Compare changes on your own traffic#

Create an experiment with 2–10 weighted variants and one control in the workspace. Compare models, system instructions or techniques. Add X-CI-Experiment with its slug; sticky assignment also needs X-CI-End-User. Draft, paused and completed experiments leave requests unchanged. Explicit completion stops new assignments and preserves results and in-flight settlements; a completed experiment can promote one variant for later traffic but cannot resume. Unknown or foreign slugs return 404. Explicit technique lists override account settings; null inherits them and [] disables optional techniques.

Create a draft in Experimentswith 2–10 weighted variants and one control. Compare model, system instructions, or techniques. Start the experiment, then attach its slug to inference requests. Sticky assignment also needs a stable, non-sensitive end-user identifier.

Assign a request to an experimentShell
curl "$ROUTER_BASE_URL/v1/chat/completions" \
  -H "Authorization: Bearer $ROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-CI-Experiment: your-experiment-slug" \
  -H "X-CI-End-User: stable-user-id" \
  -d '{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"Summarize this idea."}],"max_tokens":128}'

Draft and paused experiments leave the request unchanged. A promoted experiment uses its selected variant for all future matching requests. Unknown or another account’s slugs return 404. Fixed model variants cannot also explicitly enable model routing.

experimentscURL
curl 'https://your-router-domain/v1/experiments' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Experiment creation, draft editing, start, pause, promotion, and draft deletion use the signed-in workspace API. The complete configuration is limited to 150 KiB, with at most 12,000 characters per system prompt. Read the OpenAPI specification for the full management contract.

Use the tools you already know#

Use the integration guides for Codex through Responses, Claude Code through Anthropic Messages, or Cursor through its OpenAI-compatible base URL. Guides use this deployment origin and configured model IDs. Keep API keys in local environment variables or server settings rather than application source. Validate the actual model and tool combination you plan to use.