S
supero.docs
Developers/API Reference/Authentication

Authentication

How to authenticate against the Supero API with a login-issued JWT or a long-lived API key, and how to manage token lifecycle.

Overview

Every Supero API request (other than login and a handful of public endpoints) must be authenticated. There are two supported methods, and you can mix them depending on the caller:
  • •Bearer JWT — obtained from POST /api/v1/auth/login. Best for interactive users and short-lived sessions (browser apps, CLI tools, admin scripts).
  • •API key (X-API-Key header) — a long-lived opaque secret starting with ak_, minted per domain. Best for servers, backend jobs, and service-to-service calls where there's no human logging in.
All requests go to the base URL https://api.supero.dev. Scope (which domain/project/tenant a request operates on) comes from the JWT claims or the API key itself, optionally narrowed with project and tenant query parameters — you never need to pass separate project or tenant ID headers.
MethodHeaderTypical caller
JWT (access token)Authorization: Bearer <access_token>Logged-in users, browser/CLI sessions
API keyX-API-Key: ak_...Backend services, scripts, CI, server-to-server

⚠️ Don't mix the two

An API key is an opaque secret — send it in the X-API-Key header, never as a Bearer token. A JWT access token goes in Authorization: Bearer, never in X-API-Key.

Logging in

POST /api/v1/auth/login exchanges an email and password for a session (an access token plus a refresh token). Domain is optional — if you omit it, Supero resolves it from the email's registered domain automatically. You can optionally narrow the login to a specific project and tenant.
bash
curl -X POST https://api.supero.dev/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "your-secure-password",
    "domain": "acme-corp",
    "project": "default-project",
    "tenant": "northwind"
  }'
FieldRequiredDescription
emailYesThe account email (username is also accepted).
passwordYesThe account password.
domainNoDomain name to authenticate against. If omitted, Supero looks up the account's registered domain.
projectNoProject name to scope the session to. Defaults to the default project.
tenantNoTenant name to scope the session to. Defaults to the default tenant.

ℹ️ domain_name is accepted as an alias here

Older examples and some client code send domain_name on login instead of domain. Both work on this endpoint and mean the same thing; domain takes precedence if you send both. domain is the spelling used throughout these docs. Note that this is specific to login — user registration takes domain_name, not domain.

Response

A successful login returns a top-level auth block, a user block, a context block, and a policy block. The access token lives at auth.access_token — not at the top level.
json
{
  "auth": {
    "access_token": "eyJhbGciOiJIUzI1NiIs...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
    "token_type": "Bearer",
    "expires_in": 28800,
    "session_id": "a1b2c3d4-..."
  },
  "user": {
    "user_uuid": "5e8f...",
    "email": "[email protected]",
    "role": "developer",
    "base_role": "developer",
    "domain_name": "acme-corp",
    "project": "default-project",
    "tenant_name": "northwind",
    "permissions": ["crud:create", "crud:read", "..."]
  },
  "context": {
    "domain": "acme-corp",
    "domain_uuid": "...",
    "project": "default-project",
    "project_uuid": "...",
    "tenant": "northwind",
    "tenant_uuid": "...",
    "schema_namespace": "acme_corp",
    "surface": "admin"
  },
  "policy": { "role": "developer", "entities": { "...": "..." } }
}

ℹ️ API key for admin roles

For domain_admin, project_admin, platform_admin, and platform_user, the auth block also includes api_key — the tenant's API key — so admin-level sessions can bootstrap service credentials without a separate mint call.

Using the access token

Send the access token on every subsequent request as a standard Bearer token.
bash
curl https://api.supero.dev/api/v1/crud/acme-corp/project \
  -H "Authorization: Bearer $ACCESS_TOKEN"
The Python SDK handles this for you automatically once you're logged in.
python
from supero import login

org = login(
    domain_name="acme-corp",
    email="[email protected]",
    password="your-secure-password",
)

# org.crud.* calls now carry the session's access token automatically
products = org.crud.list("product", limit=20)

Token lifecycle: refresh and logout

TokenLifetime
access_token8 hours (expires_in: 28800 seconds)
refresh_token7 days
When the access token nears expiry, exchange the refresh token for a new access token instead of logging in again. Send the refresh token as the Bearer credential on the refresh call.
bash
curl -X POST https://api.supero.dev/api/v1/auth/refresh \
  -H "Authorization: Bearer $REFRESH_TOKEN"
json
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 28800
}
To end a session explicitly (invalidate the access token and its session), call logout with the current access token.
bash
curl -X POST https://api.supero.dev/api/v1/auth/logout \
  -H "Authorization: Bearer $ACCESS_TOKEN"
json
{
  "message": "Logged out successfully",
  "session_id": "a1b2c3d4-..."
}

💡 Check who you are

GET /api/v1/auth/me returns the current user's profile for a given access token, and GET /api/v1/auth/session returns the current session's details — handy for verifying a token is still valid before a long-running job starts.

Access token claims

The access token is a standard JWT. You can decode the payload client-side to read context without an extra round trip, but always let the server validate the signature — never trust an unverified decode for authorization decisions.
ClaimDescription
user_uuidUnique identifier of the authenticated user.
usernameThe user's username/email.
roleThe user's role (see Roles below).
session_idIdentifier of the session this token belongs to; used for refresh and logout.
token_type"access" or "refresh".
permissionsList of action-string capabilities derived from the role.
domain_uuid / domain_nameThe domain this token is scoped to.
project_uuid / project_nameThe project this token is scoped to, if any.
tenant_uuid / tenant_nameThe tenant this token is scoped to, if any.
schema_namespaceThe schema namespace resolved for this user's domain.
iat / nbf / expStandard issued-at, not-before, and expiration timestamps (Unix epoch seconds).

API keys

API keys are for callers that aren't a logged-in human — backend services, scheduled jobs, CI pipelines. A key is an opaque secret string starting with ak_; treat it as a bearer credential and don't attempt to parse structure out of it.

Mint a key

🚨 Minting against default-tenant gives you a project-wide key

Read this before you copy the call below. A key's reach comes from the tenant it is issued against, not from a setting. A key minted against any project's default-tenant resolves to project_admin and can read every tenant in that project — which is what you want for a build or admin credential, and exactly what you do not want for anything your application runs with. For a credential confined to one customer's data, mint it against a named tenant you created. See Building Multi-Tenant Apps.
bash
curl -X POST https://api.supero.dev/api/v1/domains/acme-corp/api-key \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "project_name": "default-project",
    "tenant_name": "default-tenant",
    "environment": "prod",
    "description": "billing worker"
  }'
json
{
  "success": true,
  "message": "API key generated",
  "api_key": "ak_9f1c...",
  "key_uuid": "d4e5f6...",
  "project_name": "default-project",
  "tenant_name": "default-tenant",
  "environment": "prod",
  "warning": "Save this API key securely - it will not be shown again"
}

🚨 Shown once

The plaintext key is only ever returned in the mint (and rotate) response. Store it in a secrets manager immediately — Supero cannot show it to you again.

Use a key

bash
curl https://api.supero.dev/api/v1/crud/acme-corp/order \
  -H "X-API-Key: ak_9f1c..."
python
from supero import connect

org = connect(domain_name="acme-corp", api_key="ak_9f1c...")
orders = org.crud.list("order", limit=50)

Rotate a key

Rotation issues a new secret for the same key_uuid — every reference to that key elsewhere (service configs, connectors) stays valid; you only need to swap the secret value.
bash
curl -X POST https://api.supero.dev/api/v1/auth/api-key/d4e5f6.../rotate \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"rotated_by": "[email protected]"}'
You can also list, fetch, and delete API keys through the standard CRUD surface: GET/DELETE /api/v1/crud/{domain}/api_key and /api/v1/crud/{domain}/api_key/{uuid}.

Roles

Every user and API key carries exactly one role, which determines both what they can do and the scope of data they can see. Role checks apply on top of — not instead of — the domain/project/tenant scope already carried by the token.
RoleScopeTypical use
domain_adminDomainOwns the domain; full access across all projects and tenants.
domain_userDomainViews all projects/tenants in the domain, full CRUD, no admin actions.
project_adminProjectManages all tenants within a project; defines schemas.
project_userProjectViews all tenants in a project, full CRUD, no admin actions.
developerProjectTechnical role for schema, SDK and CRUD work. Scoped to its own tenant for record access — it is not cross-tenant.
tenant_adminTenantManages a specific tenant; uses schemas (doesn't define them).
tenant_userTenantRegular tenant member with full CRUD, no admin actions.
viewerTenantRead-only access within a tenant.
platform_admin and platform_user are reserved for Supero-operated infrastructure and are not assignable to customer accounts.

Security best practices

  • •Always call the API over HTTPS; never send an access token or API key over plaintext HTTP.
  • •Store API keys and refresh tokens in a secrets manager or environment variables — never commit them to source control.
  • •Prefer the shortest-lived credential that works: a per-request access token for interactive flows, a scoped API key for long-running services.
  • •Assign the least-privileged role that lets the caller do its job — use viewer for read-only integrations rather than a broader role.
  • •Rotate API keys periodically and immediately after any suspected exposure; rotation preserves the key_uuid so you only need to update the secret value.
  • •Call logout to invalidate a session explicitly when you're done with it, rather than letting the access token merely expire.

Next steps