Live Infrastructure Across Europe • Portugal • Finland • Bulgaria
Digital Frontier
Docs
//flynn

Flynn API reference

Endpoints, limits, errors and response shapes of the Flynn API

Docs are being migrated. These are the getting-started guides. Product names have been updated to DigitalFrontier, but the SDK, CLI and config identifiers in code samples still reference their current names — we publish the surface that actually exists rather than a renamed one. The full API reference is being moved to its own documentation site.

Describes Flynn f085af34ff1f, generated 2026-10-09. It is regenerated from the running service on every production deploy.

Compatibility and deprecation

What stays stable. These keep working as they do today:

  • POST /v1/chat/completions (OpenAI-compatible), POST /v1/messages (Anthropic-compatible) and GET /v1/models.
  • Authentication with an API key, sent as Authorization: Bearer or x-api-key.
  • The error envelopes: the OpenAI shape (error.message, error.type, error.param, error.code) on /v1/chat/completions, the Anthropic shape (type: "error", error.type, error.message) on /v1/messages, and the meaning of each documented reason.

What can change without notice.

  • Which models GET /v1/models lists. A model can be added or removed at any time, so check the list rather than assuming a model is there.
  • Which model the flynn router picks for a request, and how it picks.
  • List prices, published as pricing on each model in GET /v1/models.
  • Informational fields in a model's flynn object, such as display_name, details, tags and cost.
  • Anything added: new endpoints, response fields, headers or reason values. Your client should ignore what it does not recognise.

Breaking changes. Removing or renaming anything listed as stable, or changing what an existing reason means, gets 90 days' notice. A fix for a security problem may ship with less notice.

Where changes are announced. In the Flynn API changelog, with the date each change takes effect.

Questions. Write to support@digitalfrontier.so, and include the X-Flynn-Request-Id header from the response you are asking about.

Generated from the running Flynn service, so it describes what the API does today.

Before you start

Which host

Call the API at api.flynn.digitalfrontier.so. flynn.digitalfrontier.so is the web console, not the API: /v1/* there answers with the console rather than JSON.

What the model id is

Each backend in GET /v1/models has its worker id as id; the underlying model name is at .flynn.model. To find a model by name, filter on .flynn.model, not .id. The router itself is listed as flynn (plus its named versions), marked "flynn_router": true.

A model we run ourselves may also carry .flynn.display_name, the base model without quantization or deployment suffixes (the label the console's Models page shows), and .flynn.details with context_window, hosting, quantization. They are labels only: pin a model by .id, which is unchanged. A model without them is shown by its id.

Limits

Your own values are in limits on GET /api/v1/me and GET /v1/usage (see the divergence note under Response shapes). This section documents the MECHANISM.

reasonstatusresets
rpm_window429at the next minute boundary
tpm_window429at the next minute boundary
monthly_token_cap429when the calendar month rolls over (UTC)
monthly_cost_cap429when the calendar month rolls over (UTC)
  • Requests per minute: when neither your key nor your tenant sets one, the default is 120.
  • Per-minute limits are counted per API replica, in process memory, so with several replicas the fleet-wide ceiling is a multiple of the number shown. The token limit is applied one request late: tokens are counted after a request completes.
  • Monthly caps are enforced.
  • Keys: there is no cap on the number of active API keys.
  • A self-serve tenant starts with: rpm_limit = 30, tpm_limit = None, monthly_token_cap = 5000000, monthly_cost_cap_usd = 25.0. None means no limit.

Pricing

  • Currency: USD. You are billed per token, with input and output tokens priced separately, in USD per 1M tokens.
  • Per-model list prices are the pricing object on each model in GET /v1/models, called with your API key, and are shown on the console's Models page when you are signed in. pricing.prompt is the input price and pricing.completion the output price, as decimal strings in USD per 1M tokens. Prices can change, so read them from there rather than copying them.
  • The price shown is the price billed.
  • A request sent to the router is billed at the list price of the model that served it.
  • Models without a pricing object have no list price. They are billed at provider cost plus a margin.
  • Failed requests are not billed.
  • Monthly caps are described under Limits above.
  • Prepaid credit is added by top-up from the console, paid through Revolut hosted checkout, between 5 and 500 USD per top-up. Where top-ups are not yet offered, contact support@digitalfrontier.so to add credit.

Errors

Limit and capacity refusals carry a machine-readable reason in the error body (and Retry-After where a retry can succeed). Switch on reason, not the message:

reasonmeaning
monthly_cost_capYour monthly spend allowance is used up. It resets with the calendar month.
monthly_token_capYour monthly token allowance is used up. It resets with the calendar month.
rpm_windowToo many requests in the current minute. Retry after the Retry-After header.
shared_quota_exhaustedA shared upstream budget is spent. Retrying before Retry-After will not help.
shared_quota_unavailableThe shared budget could not be checked, so the request was refused rather than risk overspending.
tpm_windowToo many tokens in the current minute. Retry after the Retry-After header.
upstream_failedEvery model backend that could serve the request failed.
wall_clock_capThe request ran past the gateway's time limit and was cancelled.
worker_blockedThe backend for this model is busy. Retryable; honour Retry-After.

Failed requests in GET /api/v1/me/requests carry an error_class:

error_classmeaning
authAuthentication or authorization failed.
client_disconnectThe client closed the connection before the response finished.
context_overflowThe request did not fit the model's context window.
invalid_requestThe request itself was malformed or unsupported.
monthly_capRefused by a monthly token or spend cap.
rate_limitRefused by a per-minute request or token limit.
shared_quotaRefused because a shared upstream budget was spent or unavailable.
unclassifiedFailed for a reason not yet classified.
upstream_failedThe model backend failed.
wall_clock_capCancelled at the gateway's time limit.
worker_blockedThe model backend was busy and admission refused the request.

Messages raised directly in a customer route's handler or in the auth, scope and limit checks every customer request passes, read from the source (messages raised in deeper helpers are not scanned). … stands for a value filled in at run time:

statusmessage
400after must be strictly less than before (got after=…, before=…)
400error_class must be one of …; got …
400key must be a 32-char uuid hex
400status=ok rows carry a NULL error_class; the two filters together always describe an empty window
400status must be 'ok' or 'error'
400amount_usd ∈ [5, 500]
400bad after cursor
400bad before cursor
400days must be in [1, 90]
400dep_limit must be in [1, 100]
400expires_days ∈ [1, 365]
400filter must be 'active', 'revoked', or 'all'
400limit must be in [1, 100]
400monthly_cost_cap_usd exceeds the tenant limit …
400monthly_cost_cap_usd must be greater than 0; omit it to inherit the tenant cap
400rpm_limit must be at least 1; omit it to inherit the tenant limit
400rpm_limit … exceeds the tenant limit …
400unknown scope(s): …. known scopes: …
401invalid or missing API key
403self-service keys are disabled
403this endpoint is internal
403this key is scope-restricted and the requested path is not available to any scope
403this key lacks the '…' scope required for …
404key not found
404top-up not found
409this top-up is …, not pending; it cannot be cancelled
409this top-up's payment is already under way; it will be credited when it completes
409top-ups are not available yet; contact support to add credit
429monthly cost quota exceeded ($…/$…)
429monthly token quota exceeded (…/…)
429rate limit exceeded
429token rate limit exceeded
502the payment provider refused this top-up; nothing was charged
503authentication temporarily unavailable
503checkout is unavailable right now; nothing was charged, please try again
503serving auth not configured

Not listed: 0 message(s) built at run time, and 0 withheld because their text would name internal detail.

Endpoints

15 endpoints.

OpenAI- and Anthropic-compatible surface (API key)

endpointmethodauthrequest shaperesponse shapedescription
/v1/chat/completionsPOSTAPI keyno request body declared in the schemaread from handler (below)Chat Completions
/v1/messagesPOSTAPI keyno request body declared in the schemaread from handler (below)Create Message
/v1/messages/count_tokensPOSTAPI keyno request body declared in the schemaread from handler (below)Count Tokens
/v1/modelsGETAPI key or dashboard sessionno request bodyread from handler (below)List Models
/v1/usageGETAPI keyno request bodyread from handler (below)My Usage

Account surface (dashboard session)

endpointmethodauthrequest shaperesponse shapedescription
/api/v1/meGETdashboard sessionno request bodyread from handler (below)Whoami
/api/v1/me/billingGETdashboard sessionno request body · query: dep_cursor, dep_limit, day_cursor, daysread from handler (below)My Billing
/api/v1/me/billing/topupPOSTdashboard sessionTopUpInread from handler (below)Request Topup
/api/v1/me/billing/topup/{intent_id}/cancelPOSTdashboard sessionno request body declared in the schemaread from handler (below)Cancel Topup
/api/v1/me/keysGETdashboard sessionno request body · query: limit, cursor, filterread from handler (below)My Keys
/api/v1/me/keysPOSTdashboard sessionMeKeyInread from handler (below)Mint My Key
/api/v1/me/keys/{key_id}DELETEdashboard sessionno request bodyread from handler (below)Revoke My Key
/api/v1/me/keys/{key_id}/rotatePOSTdashboard sessionno request body declared in the schemaread from handler (below)Rotate My Key
/api/v1/me/modelsGETnone (dashboard session optional)no request bodyread from handler (below)My Models
/api/v1/me/requestsGETdashboard sessionno request body · query: limit, before, after, key, status, error_class, cursorread from handler (below)My Requests

Response shapes

The fields each endpoint returns.

GET /api/v1/me

  • tenant: object — id, name, created
  • limits: object — monthly_token_cap, monthly_cost_cap_usd, rpm_limit
  • effective_limits
  • month

GET /api/v1/me/billing

  • balance_uact
  • balance_usd
  • billing_policy
  • payment_reference
  • provider_configured
  • provider_state
  • deposits: array of objects — id, delta_uact, delta_usd, reason, ref, status, ts
  • deposits_next_cursor
  • spend_by_day: array of objects — day, requests, total_uact, total_usd
  • spend_next_day_cursor: object (may be null) —
  • ledger: array of objects — id, delta_uact, delta_usd, reason, ref, status, ts

POST /api/v1/me/billing/topup

  • id
  • amount_usd
  • status
  • provider
  • redirect_url

POST /api/v1/me/billing/topup/{intent_id}/cancel

  • id
  • status
  • replayed

GET /api/v1/me/keys

  • keys: array of objects — id, prefix, name, created, revoked, last_used
  • next_cursor
  • counts

POST /api/v1/me/keys

  • id
  • api_key
  • prefix
  • name
  • warning

DELETE /api/v1/me/keys/{key_id}

  • id
  • revoked

POST /api/v1/me/keys/{key_id}/rotate

  • id
  • api_key
  • prefix
  • rotated_from

GET /api/v1/me/models

  • object
  • data

GET /api/v1/me/requests

  • requests
  • next_cursor: object (may be null) —

POST /v1/chat/completions

  • OpenAI Chat Completions response (proxied verbatim; SSE when stream: true)

POST /v1/messages

  • Anthropic Messages response (proxied verbatim; SSE when stream: true)

POST /v1/messages/count_tokens

  • input_tokens

GET /v1/models

  • object
  • data
  • error: object — message, type, code

GET /v1/usage

  • tenant: object (may be null) — id, name, created
  • tenant_id
  • month
  • limits: object — monthly_token_cap, monthly_cost_cap_usd, rpm_limit
  • daily: array of objects — day, model, requests, prompt_tokens, completion_tokens, billed_usd

⚠️ /v1/usage and /api/v1/me return overlapping, non-identical data

Each describes the tenant that authenticated you, and that is not always the same tenant. /v1/usage resolves it from your API key; /api/v1/me resolves it from your dashboard login, provisioning a personal tenant on first sight. A key that an operator minted for you belongs to a tenant you do not own, so the two can describe different tenants for the same person.

They also do not agree on what they include. This reference states the difference rather than smoothing it over — papering over a real inconsistency would teach you something false about the API.

  • only in /api/v1/me: effective_limits
  • only in /v1/usage: tenant_id, daily
  • limits only in /api/v1/me: —
  • limits only in /v1/usage: —

limits shares field names but not sourcing rules. On /v1/usage, rpm_limit and monthly_cost_cap_usd come from your API KEY where the key sets them and from its tenant otherwise, while monthly_token_cap is always the tenant's. On /api/v1/me all of them are the tenant's. A key may be stricter than its tenant, so the same field name can carry different numbers on the two endpoints at the same moment, and nothing in either payload says which rule produced a given value.

tenant_id on /v1/usage duplicates tenant.id and is transitional. Prefer tenant.id.

Where a request-body cell says the schema does not declare one, the endpoint may still accept a body: the cell names the gap rather than implying it takes nothing.