Quota
/ docs
Dashboard
Docs/Concepts/Tokens and actors

Tokens and actors

The credentials you'll touch when integrating Quota. Each one represents a different actor and travels on different requests. Mixing them up is the most common cause of the “why is the wrong wallet being charged” class of bug.

The one-line rule

Whatever bearer token you put in Authorization is who gets billed. If you pass a developer API key, the developer pays. If you pass an end-user OAuth access token, that user pays.

01 At a glance

TokenLooks likeRepresentsUsed atBills
Developer sessionsess_…You, logged in to the dashboard/developers/*, /account/*n/a (management)
Developer API keysk-quota-…Your app server/v1/*Your developer wallet
OAuth client secretquota_secret_…Your app, exchanging an OAuth code/oauth/tokenn/a (auth)
User access tokenquota_token_…An end-user who consented/v1/*, /v1/balanceThat user's wallet
User refresh tokenquota_refresh_…Your server, renewing a user token/oauth/tokenn/a (auth)

02 Developer session

A sess_… token is what POST /auth/login returns when you sign in as a developer. It authenticates you, the human running the dashboard or a deployment script — never an end-user. Use it to create OAuth apps, mint API keys, configure scopes, manage webhooks, and read account-level data.

Never ship a developer session into a client app or commit one to a public repo. They are full-power and they only revoke when you sign out or rotate.

# Get a developer session
curl -X POST https://api.usequota.ai/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"…"}'

# → { "session_token": "sess_5d8c7e1f…" }

# Use it on developer endpoints
curl https://api.usequota.ai/developers/apps \
  -H "Authorization: Bearer sess_5d8c7e1f…"

The session also works as a cookie (quota_session=sess_…) on the dashboard. The bearer form is what most agentic setup scripts will use.

03 Developer API key

An sk-quota-… key is what you mint from the dashboard or POST /developers/keys. It authenticates your app server for model calls and spends your developer wallet — that's the developer billing mode. For end-user-pays flows, swap the API key for an end-user OAuth access token instead (see Connect Quota Wallet or Sign in with Quota).

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.QUOTA_API_KEY,        // sk-quota-…
  baseURL: "https://api.usequota.ai/v1",
});

// Bills your developer wallet
const r = await client.chat.completions.create({
  model: "gpt-4o-mini",
  messages: [{ role: "user", content: "Hi" }],
});

04 OAuth client secret

A quota_secret_… is the server-side half of your OAuth app credentials. It only ever appears on POST /oauth/token when your server is trading an authorization code for an access token, or refreshing one. Treat it like a password — server-only, never bundled with client code.

curl -X POST https://api.usequota.ai/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=AUTH_CODE_FROM_CALLBACK" \
  -d "redirect_uri=https://yourapp.com/auth/quota/callback" \
  -d "client_id=quota_client_…" \
  -d "client_secret=quota_secret_…"

05 User OAuth tokens

After a user completes /oauth/authorize and your server exchanges the code, you get back two tokens:

  • access_token (quota_token_…): use as the bearer on the user's requests to /v1/*. Quota debits that user's wallet. Short-lived (typically one hour).
  • refresh_token (quota_refresh_…): use on POST /oauth/token with grant_type=refresh_token to get a fresh access token. Long-lived. Store server-side, never in a browser cookie in production.
import OpenAI from "openai";

// Same SDK, same baseURL — only the apiKey changes.
const client = new OpenAI({
  apiKey: userAccessToken,                  // quota_token_…
  baseURL: "https://api.usequota.ai/v1",
});

// Bills the user's wallet, not yours
const r = await client.chat.completions.create({
  model: "gpt-4o-mini",
  messages: [{ role: "user", content: "Hi" }],
});

See Connect Quota Wallet for the full OAuth round-trip in plain Express and Next.js.

06 Common mismatches

If your bill went somewhere unexpected, it's usually one of:

  • Wrong wallet charged. You called /v1/chat/completions with sk-quota-… while expecting per-user billing. API-key auth bills the developer. Swap the bearer for a user OAuth access token (quota_token_…) to bill that user's wallet instead.
  • /v1/balance returned 0 / a stranger's amount. The endpoint returns whoever the bearer is. With sk-quota-… it's your developer balance; with quota_token_… it's that user's balance. There's no “current user” — it's always the bearer.
  • 401 invalid_token on a fresh OAuth code. Codes are single-use and expire fast. Make sure the same redirect_uri is used at /oauth/authorize and /oauth/token, and that you exchange the code before the user clicks again.
  • 403 insufficient_scope on /v1/balance. The user token doesn't carry credits.read. If you want to display the balance, request credits.read credits.spend at authorization time, not just credits.spend.
  • Committed a token by accident. Rotate immediately: API keys via the dashboard (revoke and re-mint), OAuth client secrets via secret rotation (grace-period flow), developer sessions via POST /auth/logout.