Quota
/ docs
Dashboard

BYOK (provider OAuth)

Bring-your-own-key via OAuth. Link a Quota user’s ChatGPT subscription so model requests run against their OpenAI plan instead of charging Quota credits. The user authorizes once; Quota stores the encrypted tokens and refreshes them as needed.

Currently OpenAI only
The provider link is keyed by provider, but only openai is wired up today. Anthropic and Google linking will reuse the same surface when they ship.
BYOK must be enabled on your Quota account
If BYOK is not enabled, the connect endpoint returns 503 not_configured and the callback redirects to the dashboard with ?provider_error=server_config. Contact support to enable it.

Flow#

  1. The user clicks Connect ChatGPT in your dashboard. You send the browser to GET /account/providers/openai/connect.
  2. Quota generates a one-time state (10-minute TTL), stores it server-side bound to the user, and 302-redirects the browser to OpenAI’s authorization URL with the requested scopes.
  3. The user authorizes on OpenAI; OpenAI redirects back to GET /account/providers/openai/callback?code=...&state=....
  4. Quota validates state, exchanges the code for tokens, encrypts them with PROVIDER_TOKEN_ENCRYPTION_KEY, upserts the row in user_provider_links, then redirects the browser to /account/dashboard?provider_linked=openai.
  5. Quota records a social_link_created auth event for audit.

Start the OAuth flow#

GEThttps://api.usequota.ai/account/providers/openai/connect

Issues a 302 redirect to OpenAI’s authorization URL. Requires the same session token as the rest of the Account surface.

Scopes requested: openid profile email model.request.read model.request.write. The two model.request.* scopes let Quota call OpenAI on the user’s behalf using their subscription.

Request

<a href="https://api.usequota.ai/account/providers/openai/connect">
  Connect ChatGPT
</a>

Don’t fetch this endpoint with fetch(); it’s a redirect, not JSON. Either link to it directly or open it in a popup window and listen for the dashboard redirect.

Errors

401 unauthorizedMissing or expired session token.
503 not_configuredOPENAI_OAUTH_CLIENT_ID is not set on the server.

OAuth callback#

GEThttps://api.usequota.ai/account/providers/openai/callback

OpenAI redirects to this URL after the user authorizes. You will rarely call it directly — it’s the registered redirect URI on the OpenAI OAuth app.

Query

codestringAuthorization code from OpenAI. Single-use.
statestringThe opaque value Quota issued at /connect. Validated server-side and consumed on use.
errorstringSet by OpenAI if the user denied or the upstream rejected. Quota redirects to /account/dashboard?provider_error=<error>.

Outcomes

The handler always responds with a 302 to the dashboard:

?provider_linked=openaiTokens stored, link is active. The dashboard surfaces a success toast.
?provider_error=<code>Something went wrong. Common values: access_denied (user declined), token_exchange_failed (code was invalid or expired), server_config (encryption key missing).

Errors

Two cases return JSON instead of a redirect, because they indicate a misuse of the endpoint that no UI flow should ever produce:

400 invalid_requestMissing code or state in the query string.
400 invalid_stateState unknown, already consumed, or expired (>10 min since /connect).

Disconnect#

POSThttps://api.usequota.ai/account/providers/openai/disconnect

Removes the user’s OpenAI provider link. Subsequent model requests fall back to Quota credits (the standard billing path). Records a social_link_removed auth event.

Local revoke only
Quota deletes the link row but does not call OpenAI’s revoke endpoint — the user revokes Quota’s access from inside their OpenAI account if they want to invalidate the token upstream. The encrypted tokens are wiped from Quota’s database regardless.

Request

curl -X POST https://api.usequota.ai/account/providers/openai/disconnect \
  -H "Authorization: Bearer sess_..."

Response

{
  "success": true,
  "provider": "openai"
}

Errors

404 not_foundNo active OpenAI link exists for this user.

There is no dedicated GET /account/providers/openai endpoint. The current link state is part of GET /account:

{
  "linked_providers": [
    {
      "provider": "openai",
      "subscription_tier": "plus",
      "linked_at": "2026-04-02T11:08:33.510Z",
      "status": "active"
    }
  ]
}

status is one of active, expired, or revoked. subscription_tier is best-effort and may be null if the userinfo call failed at link time — the token still works in that case.