You're on the production developer portal. The interactive demo — signup, Sumsub KYC, the purchase flow, webhooks, and the trading dashboard — is disabled here because it would create real accounts. Test those flows in the sandbox against the staging API.

Getting started

Authentication

Two layers: your app authenticates with OAuth client credentials; your end users authenticate with email + password (and optional TOTP) and carry a session token.

Get your credentials

Before any token exchange you need a client_id and client_secret. Request access, and once an operator approves you'll get a one-time link to retrieve them.

  1. Submit the Request access form with your company, contact email, and intended use.
  2. An operator provisions your network identity and approves the request (manual review).
  3. You receive an email with a one-time link that reveals your client_id and client_secret — the secret is displayed once, so store it in your secret manager.
  4. Put them in this app's env as HSC_CLIENT_ID and HSC_CLIENT_SECRET (see Quickstart).

Why approval is manual

Each tenant maps to a vanta-network entity_hotkey that is minted out-of-band on Bittensor. We provision that first, then approve your request and issue OAuth credentials scoped to your tenant.
GET/v2/apps/me
App token

Confirm which tenant a key resolves to and which scopes it has. The secret is never returned — rotate it in the admin console if lost.

200 OK
{
  "app_id": "app_...",
  "slug": "company-a",
  "name": "Company A",
  "entity_hotkey": "5ABC...",
  "allowed_scopes": ["api"],
  "active": true
}

App token (OAuth client credentials)

Exchange your client_id/client_secret for a short-lived bearer token. The body is form-encoded (application/x-www-form-urlencoded), per RFC 6749 — a JSON body is rejected with 422. Do this server-side only — never expose the secret to the browser.

POST/v2/oauth/token
Public
Form fields
grant_typestring
required
"client_credentials"
client_idstring
required
Your app's client id (hsc_…)
client_secretstring
required
Your app's secret (hsk_…)
scopestringoptionalSpace-separated. Omit to receive every scope your app holds. Scopes your app lacks are silently dropped from the grant — 400 V2_INVALID_SCOPE comes back only when none of the requested scopes is allowed. Read the scope field of the token response to see what was actually granted
curl
curl -X POST http://localhost:8000/v2/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d grant_type=client_credentials \
  -d client_id=hsc_... \
  -d client_secret=hsk_... \
  -d scope=api
200 OK
{
  "access_token": "eyJhbGciOiJI...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "api"
}

Server-side only

The client secret must never reach the browser. In this app all calls flow through lib/hsc/client.ts on the server, which caches the token automatically.

Programmatic trading keys

A third credential exists for one surface only: per-trader API keys authenticate /v2/trading (writes, reads, SSE) on their own via the X-Api-Key header, so a trader's bot never holds your client secret. Everything else on this page is unchanged for keys — they cannot reach any other endpoint. See the API keys page for minting, binding and revocation.

Details and examples: /docs/api-keys.

End-user sessions

Once your app is authenticated, you create and authenticate the people who use it. A successful signup verification or login returns a session_token to send as X-Session-Token on subsequent calls.

POST/v2/auth/signup
App token
Request body
{ "email": "trader@example.com", "password": "correct horse battery staple" }
200 OK
{
  "user_id": "usr_...",
  "email": "trader@example.com",
  "email_verified": false,
  "otp_sent": true
}
POST/v2/auth/verify-email
App token
Request body
{ "email": "trader@example.com", "code": "123456" }
200 OK
{
  "user_id": "usr_...",
  "email": "trader@example.com",
  "email_verified": true,
  "session_token": "sess_...",
  "session_expires_at": "2026-06-29T00:00:00Z"
}
POST/v2/auth/login
App token
Request body
emailstring
required
User email
passwordstring
required
User password
totp_codestringoptional6-digit TOTP if MFA is enabled
200 OK
{
  "user_id": "usr_...",
  "email": "trader@example.com",
  "session_token": "sess_...",
  "session_expires_at": "2026-06-29T00:00:00Z",
  "mfa_required": false
}

Other endpoints in this group: POST /v2/auth/resend-otp, POST /v2/auth/password-reset/request, POST /v2/auth/password-reset/confirm, POST /v2/auth/sessions/revoke (logout).

Who am I?

Verify a session and resolve the current user. Pass both the app bearer token and the user's X-Session-Token.

GET/v2/auth/me
User session
curl
curl http://localhost:8000/v2/auth/me \
  -H "Authorization: Bearer <app_access_token>" \
  -H "X-Session-Token: <user_session_token>"
lib/hsc/client.ts
import { auth } from "@/lib/hsc/client";

const me = await auth.me();
// { user_id: "usr_...", email: "...", app_id: "app_..." }
200 OK
{ "user_id": "usr_...", "email": "trader@example.com", "app_id": "app_..." }

Full API reference

Every endpoint, schema, and error code is documented in the live Swagger UI at /docs on the API. See also the Quickstart.