Sevren docsAPI reference

Account API

Wallet sign-in, sessions, holder plans and payments. The account API lives at https://api.example.com/account; the wallet routes it relies on live at https://auth.example.com/auth.

Conventions

  • Request bodies are JSON objects with Content-Type: application/json. Unknown fields and trailing data answer 400 invalid_request; bodies over 8 KiB answer 413 request_too_large.
  • Routes marked session need Authorization: Bearer sk-sv-user-…. A missing, unknown or expired token answers 401 invalid_session.
  • Money amounts are exact decimal strings in USD. Times are RFC 3339 in UTC.
  • Rate limits answer 429 rate_limited with Retry-After in seconds. They are counted per account or for the whole service, never per IP address.
  • Any route can answer 503 unavailable while a dependency is down. Retry later.

The user view

GET /account/me returns it, and so do sign-in responses (under user) and the routes that change an account.

JSON
{
  "wallet": {
    "holdingBps": 0,
    "checkedAt": "2026-10-09T12:00:00Z"
  },
  "plan": null,
  "balance": {
    "creditUsd": "5",
    "usedUsd": "0.0125",
    "reservedUsd": "0",
    "availableUsd": "4.9875"
  },
  "freeTokens": {
    "resetsAt": "2026-10-10T00:00:00Z",
    "models": []
  },
  "trial": {
    "eligible": false,
    "minSupplyBps": …,
    "models": [
      {
        "model": "sevren-lite-27b",
        "input": {"total": …, "used": 0, "remaining": 0},
        "output": {"total": …, "used": 0, "remaining": 0}
      },
      …
    ]
  },
  "createdAt": "2026-10-09T00:00:00Z"
}
User view fields
FieldMeaning
walletAlways present: {holdingBps, checkedAt} for the wallet the account signs in with. holdingBps is the wallet's share of the token's total supply in basis points (50 = 0.5%), and checkedAt is when it was last read: at the last wallet sign-in or plan refresh, in the background or with POST /account/plan/refresh. The address is never returned.
plannull, or {id, label, active}. A plan is active only while its last check is at most one hour old; an inactive plan gives no free tokens and no discount. Its free tokens per model are in freeTokens in GET /v1/catalog, and its discount, if it has one, is the tier's discountBps in GET /auth/wallet/config.
balancecreditUsd added in total, usedUsd spent, reservedUsd held by requests that are running, and availableUsd = credit − used − reserved.
freeTokensAlways present: the active plan's free tokens of the day. models lists each model the plan covers (empty without an active plan) with input (prompt) and output, each {total, used, remaining}: total per day, used what the wallet has used today, and remaining what is left now, which can be lower than total − used while requests hold part of it. resetsAt is the next 00:00 UTC. See Holder benefits.
trialAlways present: the holder free try. minSupplyBps is the share of total supply it needs (the same as trial in GET /v1/catalog; 0 means none is offered). models lists every model with free-try tokens, with input and output as {total, used, remaining}: total is the model's trialTokens, used what the wallet has used ever, and remaining is 0 unless eligible. eligible is true when the wallet's last check, at most one hour old, reaches minSupplyBps. See Holder benefits.
createdAtWhen the account was created.

Sign-in and sessions

Wallet sign-in returns a session. token is your API key; see Authentication.

JSON
{
  "token": "sk-sv-user-…",
  "expiresAt": "2026-10-23T12:00:00Z",
  "user": { …the user view… }
}

POST/account/login/wallet

AUTH: none

Signs in with a wallet and creates a new session; the first sign-in with a wallet creates its account. Body: {"assertion"}, an assertion from POST https://auth.example.com/auth/wallet/verify. The plan in the assertion replaces the stored one.

Returns 200 with a session, or 201 with "created": true when the wallet had no account yet. Errors: 401 invalid_assertion (invalid, expired or already used), 429 rate_limited (a service-wide budget), 503 wallet_unavailable (wallet sign-in is not enabled).

POST/account/logout

AUTH: session

Ends the session whose token is sent. No body. Returns 204.

GET/account/me

AUTH: session

Returns the user view.

CURL
curl https://api.example.com/account/me -H "Authorization: Bearer $SEVREN_API_KEY"

Holder plans

POST/account/plan/refresh

AUTH: session

Reads the wallet's holding again and updates the plan and free-try eligibility. Plans are also refreshed in the background every few minutes. No body. Returns the user view.

Errors: 409 wallet_expired (the auth service no longer has the wallet: sign in with it again), 503 wallet_unavailable (plans are not enabled), 429 rate_limited (6 per hour per account).

Payments

GET/account/payments

AUTH: session

Everything the web app needs to add credit: the payment contract address, the account's payment reference and the accepted assets, plus the recent payments the backend has observed. See Payments for the full flow.

Returns {"enabled", "chainId", "contract", "ref", "blockTag", "confirmations", "explorerUrl", "minUsd", "assets": [...], "payments": [...]}. When payments are off: {"enabled": false, "payments": [...]} only. Each payment has id, asset, amount, creditedUsd, status ("credited" | "unmatched"), txHash, createdAt.

Credit is added by the payments watcher at latest minus confirmations blocks (currently 900, usually 1 to 2 minutes). Safe/finalized configurations omit confirmations and wait for that block tag instead. This endpoint never moves money.

Wallet routes on https://auth.example.com

These routes run in the auth service, which never receives prompts. They produce the assertions the account routes accept. The full flow is in Authentication.

GET/auth/wallet/config

AUTH: none

What a wallet message must contain and which holder plans exist. Each tier has the minSupplyBps share of total supply it needs and, when it has one, the discountBps taken off the input and output prices of every paid model for what an account on that tier pays from credit while its plan is active (omitted when 0). When plans.enabled is false, tiers is empty and there is no token.

JSON
{
  "chainId": 4663,
  "statement": "…",
  "resourcePrefix": "urn:sevren:wallet:",
  "maxMessageAgeSeconds": 300,
  "plans": {
    "enabled": true,
    "token": "0x…",
    "tiers": [{"id": "…", "label": "…", "minSupplyBps": …, "discountBps": …}]
  }
}

POST/auth/siwe/nonce

AUTH: none

No body. Returns {"nonce", "expiresAt"}. The nonce works once and expires after a few minutes. Errors: 429 rate_limited (a service-wide budget).

POST/auth/wallet/verify

AUTH: none

Body: {"message", "signature"}: the EIP-4361 message and its personal_sign signature. Returns {"assertion", "expiresAt", "purpose", "plan", "holdingBps"}; the assertion is valid for 5 minutes and works once.

Errors: 400 invalid_resource (not exactly one wallet resource, or not urn:sevren:wallet:login), 400 unsupported_signature (for example a wallet that is not deployed yet), 401 unauthorized (bad signature, domain, chain, statement or age, or a used nonce), 429 rate_limited, 503 unavailable (the chain could not be read).