Quota
/ docs
Dashboard

Account

Read and manage the authenticated user’s account — profile, balance, billing settings, spend reporting, connected apps, and deletion. All endpoints are scoped to the user behind the session token; there is no admin variant on this surface.

Auth: session tokens only
Every endpoint here requires a Quota session token (sess_…), either as Authorization: Bearer sess_… or the quota_session cookie. API keys (sk-quota-…) and end-user OAuth tokens (quota_token_…) are not accepted — these are user-account operations, not API operations.

Get account#

GEThttps://api.usequota.ai/account

Returns the signed-in user’s profile, balance, plan, billing configuration, and any linked third-party provider subscriptions (e.g. ChatGPT via OAuth — see BYOK).

Response

{
  "id": "bc9aac4f-...",
  "email": "you@example.com",
  "balance": 8471500,
  "plan": "free",
  "created_at": "2026-01-14T18:22:09.114Z",
  "name": "Ada Lovelace",
  "avatar_url": "https://cdn.example.com/me.png",
  "user_metadata": { "company": "Analytical Engines Inc." },
  "linked_providers": [
    {
      "provider": "openai",
      "subscription_tier": "plus",
      "linked_at": "2026-04-02T11:08:33.510Z",
      "status": "active"
    }
  ],
  "billing": {
    "has_payment_method": true,
    "auto_topoff_enabled": false,
    "auto_topoff_threshold": 0,
    "auto_topoff_amount": 0
  }
}

Response fields

iduuidThe user’s Quota ID.
emailstringLogin email. Lowercased.
balanceintegerCurrent credit balance. 1,000,000 credits = $1.00. Can be negative after a streaming overshoot.
planstringPlan slug (e.g. free).
created_atISO 8601When the account was created.
namestring | nullDisplay name. Nullable until set.
avatar_urlstring | nullProfile avatar URL. Nullable until set.
user_metadataobjectFree-form JSON the user has attached to their profile. Defaults to {}.
linked_providersarrayProvider OAuth links the user has authorized — see BYOK. Each entry has provider, subscription_tier, linked_at, status.
billing.has_payment_methodbooleanTrue once the user has completed a Stripe checkout (any successful purchase saves the customer).
billing.auto_topoff_enabledbooleanWhen true and a saved card is on file, Quota auto-charges when the balance crosses the threshold.
billing.auto_topoff_thresholdintegerTrigger level in credits. When balance falls below this, auto-topoff fires.
billing.auto_topoff_amountintegerCredits to add per auto-topoff event.

Update profile#

PATCHhttps://api.usequota.ai/account

Updates the user’s profile fields. Each field is independently optional. Pass null to clear name or avatar_url; omit a field to leave it untouched.

user_metadata replaces, not merges
When user_metadata is provided, the new value fully replaces the prior object. There is no patch-merge. To add one key, read the current value, set the key, and PATCH the result.

Body

namestring | nullNew display name, or null to clear. Validated against the same rules as signup.
avatar_urlstring | nullHTTPS URL to an avatar image, or null to clear.
user_metadataobjectFree-form JSON, fully replacing the prior value. Limited in size by the user-profile validator.

Request

curl -X PATCH https://api.usequota.ai/account \
  -H "Authorization: Bearer sess_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Ada Lovelace",
    "user_metadata": { "company": "Analytical Engines Inc." }
  }'

Response

{
  "id": "bc9aac4f-...",
  "email": "you@example.com",
  "name": "Ada Lovelace",
  "avatar_url": null,
  "user_metadata": { "company": "Analytical Engines Inc." }
}

Errors

400 invalid_namename was not a string or null, or failed the profile validator.
400 invalid_avatar_urlavatar_url was not a string or null, or failed the URL validator (must be HTTPS, no localhost).
400 invalid_user_metadatauser_metadata failed validation (size, depth, or non-JSON-safe values).
404 not_foundSession points at a user row that no longer exists.

Delete account#

DELETEhttps://api.usequota.ai/account

Soft-deletes the account. The user row is retained for ledger and OAuth-audit integrity, but PII is anonymized and login is made impossible by any path.

Irreversible
There is no undelete endpoint. Foreign-keyed rows (ledger entries, OAuth tokens) remain readable for audit, but the user account itself cannot be restored through the API.

On success the server, in one transaction:

  • Sets deleted_at = NOW() on the user row.
  • Anonymizes email to deleted-<id>@quota.deleted and nulls name + avatar_url.
  • Replaces password_hash with a fresh bcrypt hash over a random 32-byte string — defence in depth, the deleted_at IS NULL filter on login already blocks re-auth.
  • Revokes every active session and non-revoked OAuth token for the user.
  • Sends a confirmation email to the original address (best-effort — a delivery failure does not roll back the deletion).

Body

passwordstringrequiredThe user’s current password, re-confirmed via bcrypt against the stored hash. Required even if the user is mid-session.

Request

curl -X DELETE https://api.usequota.ai/account \
  -H "Authorization: Bearer sess_..." \
  -H "Content-Type: application/json" \
  -d '{ "password": "your-current-password" }'

Response

{
  "ok": true,
  "message": "Your account has been deleted."
}

Errors

400 invalid_passwordPassword missing, empty, or did not match the stored hash. Also returned when the session points at an OAuth-only user with no password set, so the response shape is the same in either case (no enumeration oracle).
401 unauthorizedMissing or expired session token.

Update settings (auto top-off)#

PATCHhttps://api.usequota.ai/account/settings

Updates the user’s auto top-off configuration. Auto top-off, once enabled and with a saved card on file, charges the user’s Stripe customer when their balance falls below auto_topoff_threshold, adding auto_topoff_amount credits per event.

Saved card required
Auto top-off can be toggled on without a saved card, but it will not actually charge until the user has completed at least one Stripe Checkout (which saves the customer). Check billing.has_payment_method on GET /account.

Body

auto_topoff_enabledbooleanMaster switch. When false, the threshold and amount are kept but the trigger is off.
auto_topoff_thresholdintegerTrigger level in credits. Must be >= 0. Balance falling below this fires a top-off.
auto_topoff_amountintegerCredits to add per top-off event. Must be >= 0.

Request

curl -X PATCH https://api.usequota.ai/account/settings \
  -H "Authorization: Bearer sess_..." \
  -H "Content-Type: application/json" \
  -d '{
    "auto_topoff_enabled": true,
    "auto_topoff_threshold": 1000000,
    "auto_topoff_amount": 5000000
  }'

Response

{
  "auto_topoff_enabled": true,
  "auto_topoff_threshold": 1000000,
  "auto_topoff_amount": 5000000
}

Errors

400 invalid_thresholdauto_topoff_threshold was negative.
400 invalid_amountauto_topoff_amount was negative.

Get spend time series#

GEThttps://api.usequota.ai/account/spend

Returns daily credit-usage buckets over the requested window. Buckets are aligned to UTC days; days with no usage are present as zero entries so the series is contiguous.

Query

period"7d" | "30d" | "90d"Window length. Defaults to "30d".

Request

curl https://api.usequota.ai/account/spend?period=7d \
  -H "Authorization: Bearer sess_..."

Response

{
  "period": "7d",
  "total_credits_used": 142500,
  "data": [
    { "date": "2026-05-03", "credits_used": 0 },
    { "date": "2026-05-04", "credits_used": 28100 },
    { "date": "2026-05-05", "credits_used": 0 },
    { "date": "2026-05-06", "credits_used": 51200 },
    { "date": "2026-05-07", "credits_used": 0 },
    { "date": "2026-05-08", "credits_used": 63200 },
    { "date": "2026-05-09", "credits_used": 0 }
  ]
}

Errors

400 invalid_periodperiod was not one of "7d", "30d", "90d".

Get connected apps#

GEThttps://api.usequota.ai/account/apps

Returns the list of apps the user has authorized through Sign in with Quota, with each app’s 30-day credit spend and the timestamp of its most recent request. Useful for an end-user “Apps connected to your account” dashboard.

Response

{
  "apps": [
    {
      "app_id": "9f3a2b1c-...",
      "name": "Acme Writer",
      "credits_used": 612400,
      "last_used_at": "2026-05-08T19:14:02.000Z"
    },
    {
      "app_id": "5d2e8a01-...",
      "name": "QuokkaChat",
      "credits_used": 81000,
      "last_used_at": "2026-04-21T07:33:18.000Z"
    }
  ]
}

Window is fixed at 30 days. Apps with zero usage in the window are omitted.

Create billing checkout#

POSThttps://api.usequota.ai/account/billing/checkout

Creates a Stripe Checkout session for the chosen credit package and returns the URL to redirect the user to. Quota uses the request Origin header (validated against ALLOWED_ORIGINS) to build the success and cancel URLs.

Public-facing wallet
This endpoint is for the user’s own wallet — the account behind the session. The buyer is the signed-in user; the resulting credits land on their wallet.

Body

package_idstringrequiredOne of the canonical package IDs: starter, basic, plus, pro. Each maps to a credit count and USD price; call GET /v1/packages for the full table.

Request

curl -X POST https://api.usequota.ai/account/billing/checkout \
  -H "Authorization: Bearer sess_..." \
  -H "Origin: https://your-app.example.com" \
  -H "Content-Type: application/json" \
  -d '{ "package_id": "starter" }'

Response

{
  "checkout_url": "https://checkout.stripe.com/c/pay/cs_test_...",
  "session_id": "cs_test_..."
}

Errors

400 invalid_packagepackage_id missing or not one of the supported values.
502 stripe_errorStripe rejected the create-session request. The original error is logged server-side.

Account dashboard (HTML)#

GEThttps://api.usequota.ai/account/dashboard

Returns a self-contained HTML page that wires together every endpoint on this page — balance, spend chart, connected apps, provider links, and billing settings — using only the session cookie. Intended as a fallback admin surface for users who don’t have a dashboard built into the developer’s app.

The page calls the same endpoints documented above. There is no machine-readable variant.