Console
Core concepts/Data policy

Data policy

Content-free by design; what is and is not stored.

/llms.txt

The marketplace sits between your application and model providers. Its data posture is simple: content-free by design. Your prompts and the model's responses pass through the gateway on their way to and from the provider — they are never written down. What the marketplace records about a request is token counts and metadata: ids, timings, prices, error codes.

This is not a configuration option or a retention window. There is no code path in the gateway that persists request or response content, so there is nothing to opt out of and nothing to delete later. One product is different by design, and says so: Optimize stores the eval cases and candidate answers of a search, for your organization only, until you delete the search. Nothing else holds content.

The content-free rule

Every store on the request path holds metadata only:

StoreWhat it holdsContent?
Process logsrequest id, surface, model, outcome, HTTP status — every buyer-controlled string sanitized firstNever
Telemetry (analytics)ids, org/key ids, provider, deployment, model, region, attempt ordinal, outcome, error code, statuses, timestamps, latency, five token counts, request/response byte sizes, cost, provider request id (house routes only; see below)Never
Billing ledgerper-attempt row: the prices in force, reserved maximum, settled cost, token counts, HTTP status, error code, provider request id (house routes only; see below), timestamps, route and admission contextNever
Rendered pagesmodel directory, model pages, console, status — all render from metadataNever

Some details worth knowing:

  • Model names appear in logs (they are routing metadata), but every buyer-controlled string is sanitized before it touches a log line: control characters are stripped and length is capped, so request fields cannot forge log entries.
  • Error bodies never echo your input. Every error body is the gateway's own typed shape; a provider's raw error body is never relayed. When a refusal is translated across dialects or surfaced mid-stream, the gateway emits only the typed error class.
  • Byte sizes are recorded, bodies are not. Telemetry keeps request_bytes and response_bytes so throughput is measurable without storing a single byte of what was said.
  • Image bytes and revised prompts are content. On /v1/images/generations the prompt, the base64 image and any revised_prompt are never logged, never journaled and never stored — only the response byte size is.
  • Videos are content too. On /v1/videos the prompt and the video file are never logged, journaled or stored. The gateway keeps the job's id, model, length, shape, status and charge, so you can read the job and fetch the file later; the file itself streams through from the provider when you download it. A provider's error text is reduced to a code before anything keeps it.
  • The playground keeps your conversations, images and videos in your browser: conversations in local storage, images and videos in IndexedDB, filed by your sign-in, the conversation and the reply. They are never uploaded. Deleting a chat, or Clear all, deletes them.
  • A playground decision is content-free too. The state and the answers pass through the control plane and the gateway to the decision model's provider, and are never logged or stored. The gateway keeps what it keeps for every request: model, provider, token counts and cost.
  • Raw API keys never appear anywhere — not in logs, not in the ledger, not in error bodies. The gateway authenticates by comparing a SHA-256 hash.
  • The provider request id is kept on house routes only. It is the reference number a provider gives one call, so a failed call can be traced with that provider. A house route runs on the marketplace's own provider account. A call through a provider credential you connect (BYOK) never keeps the id. A provider can put the API key into that header, so the gateway drops an id that contains the key it sent, or any 16 characters of the key in a row. It also drops an id longer than 256 characters.
Note

One debug exception exists, and it is disclosed rather than hidden: setting TM_UNSAFE_LOG_BODIES=1 prints request bodies to stdout for local debugging. The flag hard-refuses to activate outside local/demo regions — a production deployment that sets it gets a logged warning and no body logging. It never touches telemetry or the ledger in any region, and an image prompt never prints even under it.

The rule is enforced by code, review and tests: the integration suite plants sentinel content in playground chats, image and video prompts, and asserts it never appears in the control plane's or the gateway's logs, or in Postgres.

What we do store

Running an account requires a small amount of real data:

DataWhyForm
Email addressSign-in and billing receiptsStored canonicalized (lowercased; gmail dots and +tags collapsed, +tags stripped elsewhere) so aliases share one account and promotional-credit eligibility
Members' emailsOrganization membership and rolesCanonicalized the same way
API keysGateway authenticationSHA-256 hash plus the first 12 characters (so the console can show you which key). The raw key is shown exactly once, at creation
Legacy and operator-issued email-link tokensRedeeming previously issued sign-in linksSHA-256 hash only, single use, 30-minute expiry
Clerk identity and session referencesConnect verified sign-ins to the same marketplace account and support sign-outClerk issuer, user/session ids, verified canonical email, organization id and session timestamps
Authentication email delivery IDsPrevent duplicate sends when the Clerk email relay is enabledMessage id, outcome and timestamp only; no email body, sign-in link or recipient in this table
Per-attempt metadataBilling and the usage you read backThe ledger row described above
Provider credentials you connect (BYOK)Routing through your own OpenAI, Anthropic, Azure or Bedrock accountEnvelope-encrypted; never returned by any API or page. A Bedrock connection stores a role ARN, never AWS keys
Braintrust API key (Optimize)Reading your evalsEncrypted; only its last four characters are ever shown
Optimize searchesComparing models on your evalsThe eval cases and the candidates' answers, scoped to your organization, deleted with the search
Video jobsFollowing a render across reloadsJob id, model, length, shape, status and charge — never the prompt or the file
App attributionOptional HTTP-Referer / X-Title headers you sendStored as-is in telemetry — send them only if you want your app identified

The marketplace does not store passwords. Clerk handles passwords, email verification and Google sign-in. Google sign-in requests basic identity information (OpenID, email and profile); it does not request access to Gmail, Drive or Calendar. The marketplace uses the verified email to find your existing account, including accounts originally created through an email link. Clerk processes authentication data under its privacy policy.

Authentication emails can be delivered through Resend, including Clerk-generated email links. The relay verifies the sender's signature and does not log or persist the email body or sign-in link. Resend processes delivery data under its privacy policy.

Account authentication

Public signup and sign-in use Clerk in the browser at Sign up and Sign in. Complete the email-verification and authentication steps shown there. The local magic-link submission and programmatic signup routes have been removed. Existing accounts and API keys are preserved; Clerk connects a verified email to the same marketplace account.

After Clerk verifies a sign-in, the marketplace issues its own seven-day signed session cookie (HttpOnly). Marketplace Sign out revokes that cookie and the linked Clerk session. Clerk-backed sessions are rechecked during authenticated requests, using cached proof for at most 30 seconds. A revoked or expired Clerk session can no longer authorize new marketplace requests after that cache expires. Other devices stay signed in.

Beside the session cookie the browser holds a sign-in hint (tm_in, the same expiry, readable by script) that says only "signed in", and a display cache (localStorage["tm-acct"]: your email and your balance as the last page showed them). They let every page draw your account menu and credits at once. Neither opens anything, and both are cleared on sign out.

Previously issued marketplace email links and operator-issued links use stored SHA-256 token hashes; a leaked database row is not a working link. These links are single-use and expire. They do not provide a public account-creation route.

See Authentication for browser onboarding and API keys.

What providers do with your prompts

The marketplace not storing content does not mean the provider serving your request stores nothing. Every provider in the catalog must declare its prompt retention in its manifest before it can serve traffic:

json
{
  "provider": {
    "id": "anthropic",
    "privacy_policy_url": "https://www.anthropic.com/legal/privacy",
    "prompt_logging": "retained"
  }
}

prompt_logging is a closed enum — "none" or "retained" — and a manifest without it (or without a privacy policy URL) fails conformance and never goes live. The flag is surfaced where you make decisions:

  • On every model page: the Data policy card says the marketplace's logs are content-free and how many of the model's providers offer zero data retention, and the providers table has a Retention column per provider.
  • In the machine-readable catalog, per provider of each model, and per host behind an aggregator as a zdr flag:
bash
curl -s https://app.routerplus.com/api/models.json | python3 -c \
  'import json,sys; [print(m["id"], "—", [(p["id"], p["prompt_logging"]) for p in m["providers"]]) for m in json.load(sys.stdin)["models"]]'

Every provider in the catalog today declares "retained", per their published policies, except Fastino, Cloudflare Workers AI, Perplexity and RouterPlus. Levanto, which serves the decision model Sage, keeps no ordinary request, but keeps the full request of a failed or slow one for 30 days to debug it, so it declares "retained" too. Fastino, which serves the decision model GLiNER-2.5-Decide, declares "none". Fastino keeps an inference unless the request says store: false, and the gateway sends store: false on every request. Fastino's terms let it train on inputs unless a team has Zero Data Retention on; our Fastino team has it on. So Fastino keeps nothing and trains on nothing. Cloudflare Workers AI, which serves the decision models Clef and Clef-flash, declares "none", on Cloudflare's own statements. Of Clef, Cloudflare says: "we don't read, store, or train on your requests or responses (unless you want to use our fine-tuning product)". We do not use that product. Cloudflare's Workers AI data-usage page says that Cloudflare does not use a customer's content to train any AI model on Workers AI, or to improve any Cloudflare or third-party service, without the customer's explicit consent. It also says that Cloudflare stores the content only if the customer uses a Cloudflare storage service (R2 or KV, for example) with Workers AI. We use none. The gateway calls the Workers AI REST API directly. It never sends Cloudflare's AI Gateway header (cf-aig-gateway-id), so our requests do not go through AI Gateway, whose logs are on by default and can hold the full prompt and response. Cloudflare's replies to our calls carry none of AI Gateway's cf-aig-* headers. Perplexity, which serves the decision model Perplexity Decider v1 27B, declares "none", on Perplexity's own statements. Its API FAQ says: "We do not retain any query data sent through the API and do not train on any of your data." It also says that the API has zero day retention of user prompt data by default, never used for AI training, and that its compute is hosted on Amazon Web Services in North America. Perplexity keeps the usage metadata it bills from, such as the number of requests and tokens (its API FAQ). The gateway calls Perplexity's API directly, with our one Perplexity key. Bespoke Labs, which serves the decision model Bespoke Nimble v3, declares "retained": Bespoke keeps API inputs, outputs and request logs for up to 30 days, to debug the service and check for abuse, and then deletes them. It also keeps the metadata it bills from (token counts, times, key id, status). Bespoke does not train on API content unless an organization opts in, and ours does not. OpenRouter publishes no retention terms for the free endpoint that serves the decision model Mercury Decide, so that deployment (openrouter-decisions-free) declares "retained", like openrouter-decisions. For a model served through OpenRouter, served_by lists the hosts that actually run it and which of them OpenRouter classifies as zero data retention; a request can pin a host with provider.upstream (see Routing policies). We report the flags; we do not editorialize them.

RouterPlus

RouterPlus is our own decision-model service. It serves our models Decider 2B and Kev 4B, and this page is its data policy. Its GPUs run in the United States. RouterPlus writes no request logs. It keeps a cache of its answers: each answer and its usage, for 600 s (10 minutes). The cache key is the RouterPlus API key, the model and the exact request body, byte for byte, so only an identical request gets a cached answer. A cache entry is never shared across RouterPlus keys, but the gateway sends every request with our one RouterPlus key: an identical request from any of our buyers within 600 s can get the cached answer, which repeats your option names. The cache is all it keeps, so the listing declares "none". RouterPlus does not use your states, questions or answers for training. The marketplace side is the same as for every provider: content-free, as this page describes.

Supply policy: where your requests can land

Models from PRC-domiciled labs (DeepSeek, Moonshot, Z.ai, MiniMax, Tencent, Xiaomi) are listed through OpenRouter, not through those labs' own APIs: the catalog has no first-party deployment for them. Which host runs a given request is OpenRouter's choice among the hosts served_by lists for the model, unless you pin one with provider.upstream. This gets revisited only if a buyer needs first-party pricing or features the hosts lack — and it would be revisited openly, not quietly.

Reading your own metadata back

Everything the marketplace knows about your requests, you can read back with your API key:

bash
curl -s -H "Authorization: Bearer $TM_API_KEY" https://api.routerplus.com/v1/usage
curl -s -H "Authorization: Bearer $TM_API_KEY" "https://api.routerplus.com/v1/generation?id=<request_id>"

Both endpoints read the billing ledger — balance, attempts, token counts, settled cost. Neither can return message content, because none was stored.

Tip

If you need to audit a specific request, keep the x-request-id response header from the original call — it is the lookup key for /v1/generation, and the id the marketplace will use if you write in about a billing dispute.

See Authentication for account and API-key access, and Errors for the error taxonomy referenced above.

Markdown source for agents: /docs/data-policy.md · index at /llms.txt