Skip to content
GuidesJuly 19, 2026

Sign in with AnyRouter: let users bring their own LLM account

If your app needs an LLM API to run, you have two bad options: ship your own keys and eat every user's inference bill, or make each user paste in a raw provider key. "Sign in with AnyRouter" is the third option — users sign in with their AnyRouter account, and their own balance powers the inference. You get a temporary, inference-only key per user and never handle model billing.

Why your app shouldn't own the inference bill

Almost every app being built today wants an LLM somewhere in it: a chat box, a summarize button, an agent that does the work. But the moment you add one, you inherit a billing problem. If you ship your own provider keys, every request your users make lands on your invoice — a single enthusiastic user can run up a bill that dwarfs whatever they pay you, and you're now in the business of metering, rate-limiting, and fraud-watching inference you never wanted to run.

The usual escape hatch is to make each user paste in a raw OpenAI or Anthropic key. That works, but it's a terrible experience: your users now manage secrets they don't understand, you store long-lived credentials that can do far more than call a model, and you're one logging mistake away from leaking them. Neither option is what you actually want, which is simply: let the user pay for their own inference, and never touch their keys.

That is exactly what "Sign in with AnyRouter" gives you. AnyRouter is the foundation your app builds around — a gateway that already has the user's account, their credits or free tier, and every model in one catalog. Add a sign-in button, the user approves once, and your app receives a temporary, inference-only key scoped to that single user. Every request it makes is billed to their account, not yours. You ship the product; AnyRouter handles the model billing, the provider failover, and the audit log.

How the flow works

It's a standard OAuth 2.1 authorization-code flow with PKCE. Your app is a public client — there is no client secret to protect, so the whole thing works from a browser-only frontend. The user is bounced to AnyRouter's consent screen, approves an inference-only scope, and your callback exchanges the returned code for a short-lived sk-ar-v1-* key.

sequenceDiagram
  participant App as Your app
  participant AR as AnyRouter
  participant User

  App->>AR: (once) POST /oauth/register {app_type:"signin"}
  AR-->>App: client_id (public, safe to embed)
  User->>App: clicks "Sign in with AnyRouter"
  App->>AR: GET /oauth/authorize?client_id&redirect_uri&code_challenge
  AR->>User: consent screen — your app name + inference scope
  User->>AR: Approve
  AR-->>App: redirect_uri?code=...
  App->>AR: POST /oauth/token {code, code_verifier}
  AR-->>App: access_token = sk-ar-v1-* (expires in 30d)
  App->>AR: POST /chat/completions (Bearer sk-ar-v1-*)
  Note over AR: billed to the user's account
One-time registration, then per-user sign-in that mints a key billed to the user.

Every endpoint below lives under https://anyrouter.dev/api/v1/mcp/oauth/* — the same unified OAuth gateway AnyRouter uses for MCP and enterprise managed access. You register once, then run the authorize/token pair per user.

Step 1 — Register your app once

Registration is open: you don't need an AnyRouter account to register an app, and there's no client secret. Send your app's name, its exact redirect URIs (https only, localhost allowed for development), and app_type: "signin".

curl https://anyrouter.dev/api/v1/mcp/oauth/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "My AI App",
    "redirect_uris": ["https://myapp.example/callback"],
    "app_type": "signin",
    "origin_url": "https://myapp.example"
  }'
POST /api/v1/mcp/oauth/register — returns a public client_id.

The response contains your client_id. It's public — embed it in frontend code without worry. Signin apps land flagged for a light admin review and show up in the AnyRouter admin console; that's how abusive apps get suspended, and it never blocks your users from signing in.

Step 3 — Exchange the code for a key

On your callback page, verify the state matches what you stored, then POST the code and the PKCE verifier to the token endpoint. It accepts both form-encoded and JSON bodies.

const params = new URLSearchParams(location.search)
if (params.get("state") !== sessionStorage.getItem("ar_state")) {
  throw new Error("State mismatch — restart sign-in")
}

const res = await fetch("https://anyrouter.dev/api/v1/mcp/oauth/token", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    grant_type: "authorization_code",
    code: params.get("code"),
    redirect_uri: "https://myapp.example/callback",
    client_id: "your_client_id",
    code_verifier: sessionStorage.getItem("ar_verifier"),
  }),
})

const token = await res.json()
// {
//   "access_token": "sk-ar-v1-...",
//   "token_type": "Bearer",
//   "scope": "inference read:profile",
//   "expires_in": 2592000
// }
saveTokenForThisUser(token.access_token)
POST /api/v1/mcp/oauth/token — returns the user's inference key.

Store one token per user. That sk-ar-v1-* value is an ordinary AnyRouter key from your app's point of view — but it can only run inference, it's scoped to this one user, and it expires (30 days by default).

Step 4 — Run inference as the user

Use the token exactly like any AnyRouter API key. Every request is metered against the signed-in user's balance — their credits, or their free tier if they're on it. You never see a model bill.

const res = await fetch("https://anyrouter.dev/api/v1/chat/completions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "google/gemini-3.5-flash",
    messages: [{ role: "user", content: "Hello from my app!" }],
  }),
})
POST /api/v1/chat/completions — billed to the signed-in user.

Because it's a real AnyRouter key, you get the whole gateway for free: any model in the catalog by id, automatic failover when a provider is rate-limited, and the OpenAI, Anthropic, and Responses dialects all served from the same endpoint. And to render a "Signed in as …" badge, the read:profile scope lets you call GET /api/v1/me for the user's display name, avatar, and plan.

What the user sees and controls

The consent screen is deliberately narrow. A sign-in token carries exactly two scopes and nothing else — requesting more has no effect, AnyRouter clamps it back to these two:

ScopeWhat it allows
inferenceCall /chat/completions, /messages, /responses, /embeddings — billed to the user
read:profileGET /api/v1/me — display name, avatar, plan

What a sign-in token can **not** do is the important part: it can't create or revoke API keys, read the user's usage or billing, change any account setting, or inherit their admin powers. There is no management access of any kind — the worst a compromised token can do is spend a bounded amount of the user's own inference budget before it expires.

Users stay in control on the other side. From **Dashboard → Connected apps** they can revoke your app at any time, which invalidates every token you've been issued for that user. Both expiry and revocation surface the same way to your code — a 401. There are no refresh tokens in v1: treat any 401 as "re-authenticate" and silently re-run the authorize flow. If the user is still signed in to AnyRouter, they're bounced through consent and back with a fresh token in seconds.

Build on the foundation, not around it

The whole point is that inference stops being your problem. You don't provision keys, you don't front the model bill, you don't store anyone's provider secrets, and you don't build a metering system. Your users bring an account they already have; AnyRouter handles the money and the models. Your app just calls an endpoint.

Full endpoint reference, scopes, and the browser snippets end to end.

Read the Sign in with AnyRouter guide

Route your first request in 2 minutes

Start free with your own keys, or top up and pay per token. Get $4/mo in credits and free models on Go — $2/mo, or free when you donate a provider key.

Start free