Hosted Users

Hosted token storage lets Quota manage OAuth tokens server-side. Your app identifies users with the X-Quota-User header instead of handling tokens directly.

How It Works

┌─────────────┐  OAuth  ┌─────────────┐
│   Your App  │───────>│    Quota    │
│             │        │ stores token│
└──────┬──────┘        └──────┬──────┘
       │                      │
       │  X-Quota-User:123    │
       │ + API Key            │
       ├─────────────────────>│  looks up linked user
       │                      │  bills user's balance
       │   <── response ──    │
       └──────────────────────┘
  1. User clicks "Connect Wallet" and completes the standard OAuth flow.
  2. Your middleware exchanges the auth code with storageMode: "hosted". Quota stores the token link server-side and returns an external_user_id.
  3. On subsequent API calls, your app sends its API key plus X-Quota-User: external_user_id.
  4. Quota resolves the linked Quota user and bills their credit balance.

Making API Calls

Include both your API key and the X-Quota-User header:

curl https://api.usequota.ai/v1/chat/completions \
  -H "Authorization: Bearer sk-quota-your-api-key" \
  -H "X-Quota-User: user_123" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o-mini",
    "messages": [{"role": "user", "content": "Hello!"}]
  }'

Credits are deducted from the linked user's balance. If the user hasn't connected yet, Quota returns a 404.

Endpoints

GET /v1/users/:external_user_id

Look up a linked user by their external ID.

curl https://api.usequota.ai/v1/users/user_123 \
  -H "Authorization: Bearer sk-quota-your-api-key"

Response:

{
  "user": {
    "id": "quota-user-uuid",
    "email": "user@example.com",
    "balance": 450
  },
  "linked_at": "2026-01-15T12:00:00.000Z"
}

DELETE /v1/users/:external_user_id/link

Unlink a user from your app. This removes the hosted token link. The user's Quota account and balance are not affected.

curl -X DELETE https://api.usequota.ai/v1/users/user_123/link \
  -H "Authorization: Bearer sk-quota-your-api-key"

Response:

{ "success": true }

This triggers a user.disconnected webhook event.

SDK Setup

Both the Core SDK and the Next.js SDK support hosted mode.

Core SDK (any framework)

Use QuotaClient with an externalUserId to make requests on behalf of a hosted user:

import { QuotaClient } from "@usequota/core";

const quota = new QuotaClient({
  apiKey: process.env.QUOTA_API_KEY,
});

// Make a request billed to the hosted user
const response = await quota.createChatCompletion({
  model: "gpt-4o-mini",
  messages: [{ role: "user", content: "Hello!" }],
}, { externalUserId: "user_123" });

Next.js SDK

The Next.js SDK handles hosted mode end-to-end. Set storageMode: "hosted" in your middleware and provide a getExternalUserId function:

import { createQuotaMiddleware } from "@usequota/nextjs";

export default createQuotaMiddleware({
  clientId: process.env.QUOTA_CLIENT_ID!,
  clientSecret: process.env.QUOTA_CLIENT_SECRET!,
  storageMode: "hosted",
  getExternalUserId: async (request) => {
    const session = await getSession(request);
    return session.userId;
  },
});

The middleware exchanges the OAuth code, Quota stores the token link, and your app receives an external_user_id cookie. Server utilities like getQuotaUser then use the X-Quota-User header automatically.

Error Responses

StatusCodeDescription
401invalid_api_keyInvalid or missing API key
404not_foundNo linked user found for this external ID
402insufficient_creditsLinked user does not have enough credits