Docs

HTTP API: users and auth settings

Manage a project's app users (create, import, ban, delete, roles, MFA reset) and its sign-in settings: providers, sessions, SMTP, email templates, hooks, OIDC and OAuth apps.

Updated Oct 18, 2026

Manage the project's app users and how they sign in. Call these on the project URL with a team member's access token; the member's role must allow the action (owners and admins for users). Guides: Users and app roles, Authentication settings reference, Emails: SMTP, templates and limits, Auth event hooks.

URL=https://<project-ref>.sovarias.in   # the project URL names the project
ANON_KEY=<anon key>
ACCESS_TOKEN=<a team member's access token>
Base URLhttps://<project-ref>.sovarias.in — your project URL from Connect.
KeysSend the project's key as apikey. Calls made for a signed-in user also send Authorization: Bearer <access token>.
ErrorsA status code and {"detail": …} — a sentence, or {code, message} for errors your code may branch on (HTTP API).

App users

Owners and admins of the organization manage the project's app users. user_id is the user's id (node_id in the listing).

GET/api/v1/projects/{project_id}/auth/users

List the project's app users, newest first.

Pages with a cursor: when more remain, the response has an X-Next-Cursor header — pass it back as cursor. The first page (no cursor) also has X-Total-Count for the same filters.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.

Path parameters

project_idstringrequired
The project.

Query parameters

limitintegeroptionaldefault 500
Users per page (1–500).
1–500
cursorstring | nulloptional
From the previous page's X-Next-Cursor.
0–200 characters
qstring | nulloptional
Part of an email, or an exact user id.
0–200 characters
statusstring | nulloptional
active or banned.
pattern `^(active|banned) HTTP API: users and auth settings | Sovaria Docs

Returns

node_idstring
The user's id — the one every other call takes as user_id, and auth.uid() in SQL.
idstring
The id of their sign-in record (rarely needed).
emailstring
is_activeboolean
false while banned.
created_at, updated_at, last_sign_in_atstring | null
ISO 8601.
mfa_enabled, phone_mfa_enabled, any_mfa_enabledboolean
Authenticator app, SMS, either.
rolesarray<string>
Their app roles in this project (Users and app roles).
role_sourcesarray<string>
Where the roles come from.
project_idstring

Responses

  • 200Success.
  • 403Only organization owners and admins can list app users.
  • 401No valid session or key.
  • Response headers: X-Next-Cursor: eyJ0cyI6… when there is a next page; X-Total-Count: 1284 on the first page.
curl -s "$URL/api/v1/projects/f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07/auth/users?limit=50&status=active" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
[
  {
    "id": "a71d0c5e-2f84-4b9a-9e13-6c5d8f2b1a47",
    "node_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
    "email": "ana@example.com",
    "is_active": true,
    "created_at": "2026-10-02T08:14:22.481Z",
    "updated_at": "2026-10-16T19:03:51.112Z",
    "mfa_enabled": false,
    "phone_mfa_enabled": false,
    "any_mfa_enabled": false,
    "last_sign_in_at": "2026-10-17T09:41:05.330Z",
    "roles": [
      "staff"
    ],
    "role_sources": [
      "project_settings"
    ],
    "project_id": "f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07"
  }
]

POST/api/v1/projects/{project_id}/auth/users

Create an app user (as an admin).

Without a password, the user signs in by magic link or invite: send_magic_link: true emails one. email_confirm: true marks the email confirmed so no verification email is needed. Roles must exist in the project's role list.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.

Path parameters

project_idstringrequired
The project.

Body

namestringrequired
Display name.
1–255 characters
emailstringrequired
Sign-in email.
passwordstring | nulloptional
Optional; at least 8 characters.
at least 8 characters
rolesarray<string>optional
App roles to give them.
send_magic_linkbooleanoptionaldefault false
Email a sign-in link now.
email_confirmbooleanoptionaldefault false
Treat the email as confirmed.
env_idstring | nulloptional
An environment of the project.

Returns

node_idstring
The user's id — the one every other call takes as user_id, and auth.uid() in SQL.
idstring
The id of their sign-in record (rarely needed).
emailstring
is_activeboolean
false while banned.
created_at, updated_at, last_sign_in_atstring | null
ISO 8601.
mfa_enabled, phone_mfa_enabled, any_mfa_enabledboolean
Authenticator app, SMS, either.
rolesarray<string>
Their app roles in this project (Users and app roles).
role_sourcesarray<string>
Where the roles come from.
project_idstring
pending_inviteboolean
A sign-in link was sent and the email isn't confirmed yet.

Responses

  • 201Created.
  • 409That email is already registered in this project.
  • 422UNKNOWN_APP_ROLE — a role isn't defined; the body lists roles.
  • 403Only organization owners and admins.
  • 429Too many sign-in links for this address or IP.
  • 401No valid session or key.
curl -s -X POST "$URL/api/v1/projects/f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07/auth/users" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name": "Ana Lima", "email": "ana@example.com", "roles": ["staff"], "send_magic_link": true}'
Response
{
  "id": "a71d0c5e-2f84-4b9a-9e13-6c5d8f2b1a47",
  "node_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "email": "ana@example.com",
  "is_active": true,
  "created_at": "2026-10-02T08:14:22.481Z",
  "updated_at": "2026-10-16T19:03:51.112Z",
  "mfa_enabled": false,
  "phone_mfa_enabled": false,
  "any_mfa_enabled": false,
  "last_sign_in_at": null,
  "roles": [
    "staff"
  ],
  "role_sources": [
    "project_settings"
  ],
  "project_id": "f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07",
  "actor_lane": "product",
  "auth_subject_type": "product_user",
  "pending_invite": true
}

POST/api/v1/projects/{project_id}/auth/users/import

Import up to 1,000 users at once, keeping their ids and password hashes (from Supabase, Firebase or your own system).

Nothing is emailed and no sign-up hooks or function triggers run. Emails already in the project are skipped. Passwords: bcrypt hashes (Supabase) are kept as they are; firebase_scrypt hashes are checked with your Firebase project's hash parameters (hash_config, stored encrypted) and upgraded to bcrypt at the user's first sign-in. Users without a password sign in by magic link or a provider. A UUID id is kept (so rows referencing the user still match); other ids (Firebase uids) get a new id and the old one is returned as source_id. The CLI does this for you: sov import supabase / sov import firebase (Migrate from Supabase or Firebase).

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.

Path parameters

project_idstringrequired
The project.

Body

usersarray<object>required
1–1000 items
9 fields
emailstringrequired
idstringoptional
Their id in the old system.
email_verifiedbooleanoptional
phonestringoptional
display_namestringoptional
password_algorithmstringoptional
One of bcrypt, firebase_scrypt
password_hashstringoptional
password_saltstringoptional
Firebase only.
sourcestringoptional
Where they came from, kept in the user's metadata.
hash_configobjectoptional
Your Firebase project's password hash parameters (Firebase console › Authentication › Password hash parameters).
4 fields
signer_keystringrequired
Base64.
salt_separatorstringrequired
Base64.
roundsintegerrequired
mem_costintegerrequired

Returns

importedarray<object>
user (email), id (their id here), source_id, password (bcrypt, firebase_scrypt or none).
skipped, failedarray<object>
user and reason.
countsobject

Responses

  • 200Success.
  • 413More than 1,000 users in one call.
  • 422hash_config is invalid.
  • 403Only organization owners and admins.
  • 401No valid session or key.
curl -s -X POST "$URL/api/v1/projects/f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07/auth/users/import" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"users": [{"id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93", "email": "ana@example.com", "email_verified": true, "password_algorithm": "bcrypt", "password_hash": "$2a$10$Q3m…"}, {"id": "kX9fF2…", "email": "li@example.com", "password_algorithm": "firebase_scrypt", "password_hash": "lSrfV1…", "password_salt": "42xEC…"}], "hash_config": {"signer_key": "jxspr8Ki0…", "salt_separator": "Bw==", "rounds": 8, "mem_cost": 14}}'
Response
{
  "imported": [
    {
      "user": "ana@example.com",
      "id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
      "source_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
      "password": "bcrypt"
    },
    {
      "user": "li@example.com",
      "id": "9e2b…",
      "source_id": "kX9fF2…",
      "password": "firebase_scrypt"
    }
  ],
  "skipped": [
    {
      "user": "old@example.com",
      "reason": "already in this project"
    }
  ],
  "failed": [
    {
      "user": "bad",
      "reason": "missing or invalid email"
    }
  ],
  "counts": {
    "imported": 2,
    "skipped": 1,
    "failed": 1
  }
}

DELETE/api/v1/projects/{project_id}/auth/users/{user_id}

Delete an app user permanently and sign them out everywhere.

Their sign-in record is removed for good and every session ends. To keep the account but stop sign-ins, ban instead.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.

Path parameters

project_idstringrequired
The project.
user_idstringrequired
The user's id (node_id).

Responses

  • 204Success, with no body.
  • 404No such user in this project.
  • 403Only organization owners and admins.
  • 401No valid session or key.
curl -s -X DELETE "$URL/api/v1/projects/f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07/auth/users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

POST/api/v1/projects/{project_id}/auth/users/{user_id}/ban

Ban an app user, or unban them — each call switches it.

A banned user can't sign in or refresh a session. is_active in the answer is the new state.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.

Path parameters

project_idstringrequired
The project.
user_idstringrequired
The user.

Responses

  • 200Success.
  • 400You can't ban yourself.
  • 404No such user.
  • 401No valid session or key.
curl -s -X POST "$URL/api/v1/projects/f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07/auth/users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/ban" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "is_active": false
}

GET/api/v1/projects/{project_id}/auth/users/{user_id}/roles

A user's app roles.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.

Path parameters

project_idstringrequired
The project.
user_idstringrequired
The user.

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/projects/f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07/auth/users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/roles" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "org_id": "b2f4a1c8-7d3e-4f0a-9c6b-5e8d2a1f3c90",
  "project_id": "f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07",
  "roles": [
    "staff"
  ]
}

PUT/api/v1/projects/{project_id}/auth/users/{user_id}/roles

Replace a user's app roles.

Takes effect at their next token (refresh, or sign-in). Roles are what ctx.actor.roles in rules and auth.jwt() in SQL see (Users and app roles).

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.

Path parameters

project_idstringrequired
The project.
user_idstringrequired
The user.

Body

rolesarray<string>optional
The full list; [] removes all.

Responses

  • 200Success.
  • 422UNKNOWN_APP_ROLE — a role isn't defined in the project.
  • 401No valid session or key.
curl -s -X PUT "$URL/api/v1/projects/f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07/auth/users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/roles" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"roles": ["staff", "billing"]}'
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "org_id": "b2f4a1c8-7d3e-4f0a-9c6b-5e8d2a1f3c90",
  "project_id": "f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07",
  "roles": [
    "billing",
    "staff"
  ]
}

GET/api/v1/projects/{project_id}/auth/users/{user_id}/mfa

Whether a user has two-factor sign-in turned on.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.

Path parameters

project_idstringrequired
The project.
user_idstringrequired
The user.

Returns

mfa_enabledboolean
Authenticator app.
phone_mfa_enabledboolean
SMS codes.
totp_enrolledboolean
An authenticator secret is stored.
any_mfa_enabledboolean

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/projects/f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07/auth/users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/mfa" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "mfa_enabled": true,
  "phone_mfa_enabled": false,
  "totp_enrolled": true,
  "any_mfa_enabled": true
}

POST/api/v1/projects/{project_id}/auth/users/{user_id}/mfa/reset

Turn off a user's two-factor sign-in (they lost their phone).

Removes their authenticator and SMS factors; they sign in with their password alone and can enrol again. Recorded in the user's security activity.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.

Path parameters

project_idstringrequired
The project.
user_idstringrequired
The user.

Responses

  • 200Success.
  • 404No such user in this project.
  • 401No valid session or key.
curl -s -X POST "$URL/api/v1/projects/f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07/auth/users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/mfa/reset" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "status": "reset",
  "message": "MFA has been reset for this user",
  "mfa_enabled": false,
  "phone_mfa_enabled": false,
  "totp_enrolled": false,
  "any_mfa_enabled": false
}

POST/api/v1/auth/product-users/{user_id}/test-session

Get a short-lived access token for one of your app users, to test your API and rules as them.

For owners and admins. The token is a real access token for that user (no refresh token), lasting minutes (default 15, at most 60), marked test_session — it can't create further test sessions. Every issue is audited.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/product-users/{user_id}/test-session

Path parameters

user_idstringrequired
The user.

Body

minutesintegeroptionaldefault 15
How long it lasts (1–60).
1.0–60.0

Responses

  • 200Success.
  • 400No organization/project scope.
  • 403Not an owner/admin, or called with a test session.
  • 404No such user.
  • 401No valid session or key.
  • 422The body is missing a required field or has a field of the wrong type.
curl -s -X POST "$URL/api/v1/auth/product-users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/test-session" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"minutes": 15}'
Response
{
  "access_token": "eyJhbGciOiJFUzI1NiIs…",
  "token_type": "bearer",
  "expires_in": 900,
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "email": "ana@example.com",
  "roles": [
    "staff"
  ],
  "test_session": true
}

Activity and data requests

GET/api/v1/auth/users/{user_id}/timeline

A user's sign-in and security events, newest first.

Event types include auth.login.success, auth.login.failed, auth.logout, auth.token.refresh, auth.password.changed, auth.password.reset, auth.mfa.enrolled, auth.mfa.verified, auth.mfa.disabled, auth.passkey.registered, auth.session.revoked, auth.email.changed.

Who
The user themselves, or an organization admin.
Also at
/auth/v1/users/{user_id}/timeline

Path parameters

user_idstringrequired
The user.

Query parameters

atstring | nulloptional
Only events at or before this time (ISO 8601).
event_typestring | nulloptional
One event type.
limitintegeroptionaldefault 50
How many (at most 200).
offsetintegeroptionaldefault 0
How many to skip.

Responses

  • 200Success.
  • 400at isn't a valid time.
  • 403Someone else's timeline without admin access.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/timeline?limit=20" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "at": null,
  "events": [
    {
      "id": "5d2f…",
      "event_type": "auth.login.success",
      "meta": {
        "method": "password",
        "aal": "aal1",
        "ip": "203.0.113.24",
        "device": "Chrome on macOS"
      },
      "created_at": "2026-10-17T09:41:05.330Z"
    }
  ],
  "count": 1,
  "limit": 20,
  "offset": 0
}

GET/api/v1/auth/users/{user_id}/timeline/state

What a user's account looked like at a moment in time (for investigations).

Rebuilt from the timeline: the last sign-in before at, whether two-factor and passkeys were set up, whether the account was banned, and which sessions were open (approximate — expired sessions aren't recorded).

Who
The user themselves, or an organization admin.
Also at
/auth/v1/users/{user_id}/timeline/state

Path parameters

user_idstringrequired
The user.

Query parameters

atstringrequired
The moment (ISO 8601); now by default.

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/timeline/state?at=2026-10-10T00:00:00Z" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "at": "2026-10-10T00:00:00+00:00",
  "last_login": "2026-10-09T18:22:10.004Z",
  "last_method": "password",
  "last_aal": "aal2",
  "was_mfa_enrolled": true,
  "had_passkey": false,
  "is_active": true,
  "active_sessions": [
    "9f1c2e…"
  ],
  "mfa_factors": [
    {
      "type": "totp",
      "enrolled_at": "2026-10-03T11:00:12.551Z"
    }
  ],
  "passkeys_registered": [],
  "total_events_at": 41
}

POST/api/v1/auth/users/{user_id}/data-export

Everything Sovaria holds about a user, as one JSON document (data-protection requests).

Covers the account, sign-in records, API keys, webhooks they own and their security events. Data in your own tables isn't included — export that with SQL.

Who
The user themselves, or an organization owner/admin for users in their organization.
Also at
/auth/v1/users/{user_id}/data-export

Path parameters

user_idstringrequired
The user.

Returns

user_idstringrequired
The app user's id.
exported_atstringrequired
profileobjectrequired
auth_methodsarray<object>required
api_keysarray<object>required
webhooksarray<object>required
auth_eventsarray<object>required

Responses

  • 200Success.
  • 400user_id isn't a UUID.
  • 403Not that user or an admin of their organization.
  • 404No such user.
  • 401No valid session or key.
curl -s -X POST "$URL/api/v1/auth/users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/data-export" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "exported_at": "2026-10-18T10:02:11.900Z",
  "profile": {
    "email": "ana@example.com",
    "is_active": true,
    "email_verified": true,
    "mfa_enabled": false,
    "created_at": "2026-10-02T08:14:22.481Z",
    "updated_at": "2026-10-16T19:03:51.112Z"
  },
  "auth_methods": [
    {
      "email": "ana@example.com",
      "org_id": "b2f4a1c8-7d3e-4f0a-9c6b-5e8d2a1f3c90",
      "project_id": "f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07",
      "env_id": null,
      "mfa_enabled": false,
      "created_at": "2026-10-02T08:14:22.481Z"
    }
  ],
  "api_keys": [],
  "webhooks": [],
  "auth_events": [
    {
      "event_type": "auth.login.success",
      "meta": {
        "method": "password"
      },
      "created_at": "2026-10-17T09:41:05.330Z"
    }
  ]
}

POST/api/v1/auth/users/{user_id}/delete-request

Start deleting a user's account: deactivated now, erased after 30 days.

The account is deactivated and every session revoked at once; the permanent deletion is scheduled 30 days later. Calling again returns the existing schedule.

Who
The user themselves, or an organization owner/admin for users in their organization.
Also at
/auth/v1/users/{user_id}/delete-request

Path parameters

user_idstringrequired
The user.

Returns

user_idstringrequired
The app user's id.
statusstringrequired
scheduled_atstringrequired
hard_delete_atstringrequired
messagestringrequired

Responses

  • 200Success.
  • 401No valid session or key.
curl -s -X POST "$URL/api/v1/auth/users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/delete-request" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "status": "scheduled",
  "scheduled_at": "2026-10-18T10:05:00.000Z",
  "hard_delete_at": "2026-11-17T10:05:00.000Z",
  "message": "Account deactivated. Permanent deletion scheduled in 30 days."
}

Sign-in methods, sessions and email

GET/api/v1/auth/providers/config

Which sign-in methods are on for the project, and the SMS settings.

enabled is true only when the method is turned on and available (requested_enabled is what you asked for). Provider ids: email, magic_link, phone_otp, github, google, gitlab, microsoft, apple, discord, linkedin, facebook, x_twitter, saml, oidc, custom_jwt. Social sign-in with your own OAuth app is set up under OIDC providers below.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/providers/config

Returns

providersarray<object>
id, label, enabled, requested_enabled, available, managed_by, description, setup_required, and unavailable_reason when it can't be used.
sms_configobject
provider (log, twilio, custom), account_sid, from_number, and whether an auth token is stored (auth_token_set, auth_token_hint). The token itself is never returned.
database_linkobject
Kept for older clients; not used by projects.

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/providers/config" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "providers": [
    {
      "id": "email",
      "label": "Email + Password",
      "enabled": true,
      "requested_enabled": true,
      "available": true,
      "managed_by": "core",
      "description": "Built-in credential authentication.",
      "setup_required": []
    },
    {
      "id": "magic_link",
      "label": "Magic Link",
      "enabled": true,
      "requested_enabled": true,
      "available": true,
      "managed_by": "core",
      "description": "…",
      "setup_required": []
    }
  ],
  "database_link": {
    "enabled": false,
    "engine": "postgresql",
    "host": "",
    "port": 5432,
    "database": "",
    "schema": "public",
    "username": "",
    "ssl": true
  },
  "sms_config": {
    "provider": "twilio",
    "account_sid": "AC5f…",
    "from_number": "+14155550123",
    "auth_token_set": true,
    "auth_token_hint": "8c…f2"
  }
}

PUT/api/v1/auth/providers/config

Turn sign-in methods on or off, and set the SMS provider for phone codes.

Send only what changes. Turning on more social providers than the plan allows is refused (auth.oauth_providers).

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/providers/config

Body

providersarray<object>optional
2 fields
idstringrequired
Provider id.
enabledbooleanrequired
sms_configobjectoptional
4 fields
providerstringoptional
log writes codes to the logs (development).
One of log, twilio, custom
account_sidstringoptional
from_numberstringoptional
auth_tokenstringoptional
Stored encrypted; omit to keep the current one.
database_linkobjectoptional
Ignored for projects.

Responses

  • 200The same shape as GET, after the change.
  • 403PLAN_LIMIT_REACHED — more social providers than the plan allows.
  • 401No valid session or key.
curl -s -X PUT "$URL/api/v1/auth/providers/config" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"providers": [{"id": "magic_link", "enabled": true}, {"id": "phone_otp", "enabled": true}], "sms_config": {"provider": "twilio", "account_sid": "AC5f…", "from_number": "+14155550123", "auth_token": "<twilio auth token>"}}'

GET/api/v1/auth/sessions/config

Your own session policy: how long sessions last and how many you may have.

Who
Any signed-in account (it applies to that account).
Also at
/auth/v1/sessions/config

Returns

timeout_minutesinteger
Access token lifetime.
refresh_daysinteger
How long a session can be refreshed.
max_concurrentinteger
Open sessions allowed (0 = no limit).
single_sessionboolean
Signing in ends the other sessions.

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/sessions/config" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "timeout_minutes": 15,
  "refresh_days": 7,
  "max_concurrent": 0,
  "single_session": false
}

PUT/api/v1/auth/sessions/config

Change your own session policy.

Who
Any signed-in account (it applies to that account).
Also at
/auth/v1/sessions/config

Body

timeout_minutesintegerrequired
Access token lifetime, 1–10080.
1.0–10080.0
refresh_daysintegerrequired
1–365.
1.0–365.0
max_concurrentintegerrequired
0–100 (0 = no limit).
0.0–100.0
single_sessionbooleanoptionaldefault false
Keep only this session.

Responses

  • 200Success.
  • 401No valid session or key.
  • 422The body is missing a required field or has a field of the wrong type.
curl -s -X PUT "$URL/api/v1/auth/sessions/config" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"timeout_minutes": 30, "refresh_days": 14, "max_concurrent": 5, "single_session": false}'
Response
{
  "timeout_minutes": 30,
  "refresh_days": 14,
  "max_concurrent": 5,
  "single_session": false
}

GET/api/v1/auth/smtp-config

The project's own SMTP server for auth emails, if set (the password is never returned).

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/smtp-config

Returns

enabledboolean
Custom SMTP is on and has a host — emails go through it.
customSmtpEnabledboolean
The switch itself.
host, port, user, from_addressstring | integer
password_set, password_hintboolean, string
Whether a password is stored, and its first and last two characters.

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/smtp-config" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "enabled": true,
  "host": "smtp.postmarkapp.com",
  "port": 587,
  "user": "pm-user",
  "from_address": "Acme <no-reply@acme.com>",
  "password_set": true,
  "password_hint": "Xy…9q",
  "customSmtpEnabled": true
}

PUT/api/v1/auth/smtp-config

Send the project's auth emails through your own SMTP server.

Send only what changes; omit password to keep the stored one. Until it's on, Sovaria's mail service sends them (Emails: SMTP, templates and limits).

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/smtp-config

Body

customSmtpEnabledboolean | nulloptional
Use your server.
hoststring | nulloptional
SMTP host.
portinteger | nulloptional
Usually 587 (STARTTLS) or 465.
1.0–65535.0
userstring | nulloptional
SMTP user.
passwordstring | nulloptional
SMTP password (stored encrypted).
from_addressstring | nulloptional
Sender, e.g. Acme <no-reply@acme.com>.

Responses

  • 200The same shape as GET.
  • 401No valid session or key.
  • 422The body is missing a required field or has a field of the wrong type.
curl -s -X PUT "$URL/api/v1/auth/smtp-config" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"customSmtpEnabled": true, "host": "smtp.postmarkapp.com", "port": 587, "user": "pm-user", "password": "<smtp password>", "from_address": "Acme <no-reply@acme.com>"}'

GET/api/v1/auth/email-templates

The project's auth email templates (yours where changed, the defaults otherwise).

Keys: email_verification, password_reset, magic_link, password_changed, new_sign_in, mfa_challenge, email_change_confirm_old, email_change_confirm_new, invite_user. {{ placeholders }} are filled when sending: project_name everywhere, plus per template confirmation_url, reset_url, magic_link_url, otp_code, code, ttl_minutes, ttl_hours, new_email, confirm_url, finalize_url, invite_url, ip, device, signed_in_at, changed_at, email.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/email-templates

Returns

<template key>object
subject, body_html, body_text.

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/email-templates" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "magic_link": {
    "subject": "Your sign-in link for {{ project_name }}",
    "body_html": "<!doctype html>…{{ magic_link_url }}…",
    "body_text": "Sign in to {{ project_name }}: {{ magic_link_url }} …"
  },
  "password_reset": {
    "subject": "Reset your {{ project_name }} password",
    "body_html": "…",
    "body_text": "…"
  }
}

PUT/api/v1/auth/email-templates/{template_key}

Change one auth email: subject, HTML and plain-text body.

Send only the parts you change. Custom templates are a paid feature (custom_email_templates).

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/email-templates/{template_key}

Path parameters

template_keystringrequired
Which email (see the list above).

Body

subjectstring | nulloptional
Subject line.
body_htmlstring | nulloptional
HTML body.
body_textstring | nulloptional
Plain-text body.

Responses

  • 200Success.
  • 400Unknown template key (the message lists the valid ones).
  • 403PLAN_CUSTOM_EMAIL_TEMPLATES_REQUIRED — not in the plan.
  • 401No valid session or key.
  • 422The body is missing a required field or has a field of the wrong type.
curl -s -X PUT "$URL/api/v1/auth/email-templates/magic_link" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"subject": "Your Acme sign-in link", "body_text": "Hi! Sign in to Acme: {{ magic_link_url }}\nThe link works for {{ ttl_minutes }} minutes."}'
Response
{
  "template_key": "magic_link",
  "subject": "Your Acme sign-in link",
  "body_html": "…",
  "body_text": "Hi! Sign in to Acme: {{ magic_link_url }}\nThe link works for {{ ttl_minutes }} minutes."
}

DELETE/api/v1/auth/email-templates/{template_key}

Go back to the default for one email (allowed on every plan).

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/email-templates/{template_key}

Path parameters

template_keystringrequired
Which email.

Responses

  • 200Success.
  • 400Unknown template key.
  • 401No valid session or key.
curl -s -X DELETE "$URL/api/v1/auth/email-templates/magic_link" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "template_key": "magic_link",
  "status": "reset_to_default",
  "subject": "Your sign-in link for {{ project_name }}",
  "body_html": "…",
  "body_text": "…"
}

Auth hooks and custom claims

GET/api/v1/auth/event-hooks

Webhooks that receive the project's auth events.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/event-hooks

Returns

delivery_count, failure_countinteger
last_errorstring | null
A short code such as http_503 or network — never the response body.

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/event-hooks" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
[
  {
    "id": "0b6e…",
    "name": "CRM sync",
    "url": "https://hooks.acme.com/sovaria-auth",
    "events": [
      "auth.user.login.success",
      "auth.user.registered"
    ],
    "enabled": true,
    "created_at": "2026-10-01T10:00:00+00:00",
    "updated_at": "2026-10-01T10:00:00+00:00",
    "delivery_count": 412,
    "failure_count": 3,
    "last_delivery_at": "2026-10-17T09:41:05+00:00",
    "last_error": null
  }
]

POST/api/v1/auth/event-hooks

Send auth events (sign-ups, sign-ins, bans…) to your URL.

Events: auth.user.registered, auth.user.login.success, auth.user.login.failed, auth.user.logout, auth.user.password_reset, auth.user.email_verified, auth.user.banned, or * for all. Up to 20 hooks per project. Each delivery is a POST of {id, type, project_id, timestamp, data}; with a secret it carries X-Sovaria-Signature: sha256=<hex HMAC of the body>. Deliveries time out after 10 s and are retried after 0.5 s, 1 s and 2 s; the sign-in itself never waits for them (Auth event hooks).

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/event-hooks

Body

namestringrequired
A label.
1–128 characters
urlstringrequired
HTTPS endpoint (private addresses are refused).
eventsarray<string>required
Event types, or ["*"].
1–… items
secretstringoptional
Signing secret (stored encrypted).
0–256 characters
enabledbooleanoptionaldefault true
On or off.
project_idstring | nulloptional
The project, if not sent as a header.

Responses

  • 201Created.
  • 400Unknown event type, a private or non-HTTPS URL, no project, or already 20 hooks.
  • 401No valid session or key.
  • 422The body is missing a required field or has a field of the wrong type.
curl -s -X POST "$URL/api/v1/auth/event-hooks" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name": "CRM sync", "url": "https://hooks.acme.com/sovaria-auth", "events": ["auth.user.registered", "auth.user.login.success"], "secret": "<random secret>"}'
Response
{
  "id": "0b6e…",
  "name": "CRM sync",
  "url": "https://hooks.acme.com/sovaria-auth",
  "events": [
    "auth.user.login.success",
    "auth.user.registered"
  ],
  "enabled": true,
  "created_at": "2026-10-18T10:10:00+00:00",
  "updated_at": "2026-10-18T10:10:00+00:00",
  "delivery_count": 0,
  "failure_count": 0,
  "last_delivery_at": null,
  "last_error": null
}

DELETE/api/v1/auth/event-hooks/{hook_id}

Remove an auth event hook.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/event-hooks/{hook_id}

Path parameters

hook_idstringrequired
The hook.

Responses

  • 204Success, with no body.
  • 404No such hook.
  • 401No valid session or key.
curl -s -X DELETE "$URL/api/v1/auth/event-hooks/0b6e…" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

GET/api/v1/auth/jwt-enrichment-hook

The custom-claims hook, or null when none is set.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/jwt-enrichment-hook

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/jwt-enrichment-hook" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "url": "https://api.acme.com/claims",
  "enabled": true
}

POST/api/v1/auth/jwt-enrichment-hook

Add your own claims to every access token: Sovaria asks your URL each time it issues one.

Your URL receives POST {user_id, project_id, claims} (signed with the secret) and answers {"claims": {…}}; those keys are added to the token's ctx. It must answer within 50 ms — otherwise the token is issued without them. Keys Sovaria owns (roles, org_id, project_id, env_id, aal, actor_lane, lane, plan, permissions…) are dropped. Needs the auth.custom_jwt_claims plan feature (Authentication settings reference).

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/jwt-enrichment-hook

Body

urlstringrequired
HTTPS URL.
secretstringoptional
HMAC-SHA256 signing secret.
enabledbooleanoptionaldefault true
On or off.

Responses

  • 201Created.
  • 400Private or invalid URL.
  • 403PLAN_FEATURE_REQUIRED — custom claims aren't in the plan.
  • 401No valid session or key.
  • 422The body is missing a required field or has a field of the wrong type.
curl -s -X POST "$URL/api/v1/auth/jwt-enrichment-hook" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"url": "https://api.acme.com/claims", "secret": "<random secret>"}'
Response
{
  "url": "https://api.acme.com/claims",
  "enabled": true
}

DELETE/api/v1/auth/jwt-enrichment-hook

Remove the custom-claims hook.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/jwt-enrichment-hook

Responses

  • 204Success, with no body.
  • 404None is set.
  • 401No valid session or key.
curl -s -X DELETE "$URL/api/v1/auth/jwt-enrichment-hook" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Social and OIDC sign-in with your own OAuth app

GET/api/v1/auth/oidc/presets

Sign-in providers you can connect with your own OAuth app, and the callback URL to register with each.

Presets: google, microsoft, apple, gitlab, linkedin, slack, twitch, github, discord, facebook, spotify, bitbucket, sovaria, and custom_oidc for any OpenID Connect provider.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/oidc/presets

Returns

kindstring
oidc or oauth2.
console_urlstring
Where to create the OAuth app.
callback_urlstring
The redirect URI to register there.

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/oidc/presets" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
[
  {
    "id": "google",
    "name": "Google",
    "kind": "oidc",
    "scopes": [
      "openid",
      "email",
      "profile"
    ],
    "console_url": "https://console.cloud.google.com/apis/credentials",
    "discovery_url": "https://accounts.google.com/.well-known/openid-configuration",
    "authorize_url": "",
    "token_url": "",
    "userinfo_url": "",
    "emails_url": "",
    "token_auth": "body",
    "mapping": {},
    "trusts_email": true,
    "note": "",
    "callback_url": "https://api.sovaria.in/api/v1/auth/sso/google/callback"
  }
]

GET/api/v1/auth/oidc/providers

The project's connected sign-in providers (secrets are never returned).

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/oidc/providers

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/oidc/providers" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
[
  {
    "preset": "google",
    "kind": "oidc",
    "discovery_url": "https://accounts.google.com/.well-known/openid-configuration",
    "issuer": "https://accounts.google.com",
    "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
    "token_endpoint": "https://oauth2.googleapis.com/token",
    "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
    "id": "google",
    "name": "Google",
    "client_id": "1234-abc.apps.googleusercontent.com",
    "allowed_domains": [],
    "scopes": [
      "openid",
      "email",
      "profile"
    ],
    "enabled": true,
    "created_at": "2026-10-05T12:00:00Z",
    "has_secret": true,
    "callback_url": "https://api.sovaria.in/api/v1/auth/sso/google/callback"
  }
]

POST/api/v1/auth/oidc/providers

Connect a sign-in provider with your own OAuth app (Google, GitHub, Apple, any OIDC provider…).

Create the OAuth app at the provider first, with the preset's callback_url as its redirect URI. OIDC providers are checked now (their discovery document must load). A preset's id becomes the provider id (google); custom_oidc gets a generated id. The number of providers is limited by the plan (auth.oauth_providers). Apps then sign users in with GET /auth/sso/{id}/authorize (Social sign-in).

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/oidc/providers

Body

presetstringoptionaldefault custom_oidc
A preset id, or custom_oidc.
namestring | nulloptional
Shown on the sign-in button.
0–128 characters
discovery_urlstring | nulloptional
https://…/.well-known/openid-configuration (required for custom_oidc).
0–2,048 characters
client_idstringrequired
From the provider.
1–512 characters
client_secretstringrequired
From the provider (stored encrypted).
1–8,192 characters
allowed_domainsarray<string>optional
Only these email domains may sign in (empty = any).
scopesarray<string> | nulloptional
Scopes to ask for (the preset's by default).
enabledbooleanoptionaldefault true
On or off.

Responses

  • 201The provider, as in the list (with has_secret: true and its callback_url).
  • 400No https discovery URL, or discovery failed (the message says why).
  • 403PLAN_LIMIT_REACHED — more providers than the plan allows.
  • 401No valid session or key.
  • 422The body is missing a required field or has a field of the wrong type.
curl -s -X POST "$URL/api/v1/auth/oidc/providers" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"preset": "google", "client_id": "1234-abc.apps.googleusercontent.com", "client_secret": "<client secret>", "allowed_domains": ["acme.com"]}'

PATCH/api/v1/auth/oidc/providers/{provider_id}

Change a provider's name, credentials, allowed domains, scopes or switch it off.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/oidc/providers/{provider_id}

Path parameters

provider_idstringrequired
The provider.

Body

namestring | nulloptional
Name.
0–128 characters
client_idstring | nulloptional
Client id.
1–512 characters
client_secretstring | nulloptional
New secret.
1–8,192 characters
allowed_domainsarray<string> | nulloptional
Domains.
scopesarray<string> | nulloptional
Scopes.
enabledboolean | nulloptional
On or off.

Responses

  • 200The provider.
  • 404No such provider.
  • 401No valid session or key.
  • 422The body is missing a required field or has a field of the wrong type.
curl -s -X PATCH "$URL/api/v1/auth/oidc/providers/google" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"allowed_domains": ["acme.com", "acme.io"]}'

DELETE/api/v1/auth/oidc/providers/{provider_id}

Disconnect a provider. Users who signed in with it keep their accounts.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/oidc/providers/{provider_id}

Path parameters

provider_idstringrequired
The provider.

Responses

  • 204Success, with no body.
  • 404No such provider.
  • 401No valid session or key.
curl -s -X DELETE "$URL/api/v1/auth/oidc/providers/google" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Your project as an OAuth provider

GET/api/v1/auth/oauth-apps

Apps that can sign in with your project as their OAuth provider ("Sign in with Acme").

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/oauth-apps

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/oauth-apps" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "server_enabled": true,
  "apps": [
    {
      "id": "7f2c…",
      "name": "Acme Analytics",
      "client_id": "sov_3f9a1c0b7e2d4a6f8c1e",
      "redirect_uris": [
        "https://analytics.acme.com/callback"
      ],
      "scopes": [
        "email",
        "openid",
        "profile"
      ],
      "enabled": true,
      "created_at": "2026-10-11T09:00:00+00:00",
      "client_secret_set": true
    }
  ]
}

PUT/api/v1/auth/oauth-server

Turn the project's OAuth server on or off.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/oauth-server

Body

enabledbooleanrequired
On or off.

Responses

  • 200Success.
  • 401No valid session or key.
  • 422The body is missing a required field or has a field of the wrong type.
curl -s -X PUT "$URL/api/v1/auth/oauth-server" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"enabled": true}'
Response
{
  "enabled": true
}

POST/api/v1/auth/oauth-apps

Register an app that signs in with your project. The client secret is shown only in this answer.

Redirect URIs must be https, or http on localhost. Scopes default to openid, profile, email. Up to 50 apps per project.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/oauth-apps

Body

namestringrequired
The app's name.
1–128 characters
redirect_urisarray<string>required
Where users return after signing in.
1–… items
scopesarray<string> | nulloptional
Scopes it may ask for.
enabledbooleanoptionaldefault true
On or off.

Responses

  • 201Created.
  • 400Invalid redirect URI, no name, or already 50 apps.
  • 401No valid session or key.
  • 422The body is missing a required field or has a field of the wrong type.
curl -s -X POST "$URL/api/v1/auth/oauth-apps" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name": "Acme Analytics", "redirect_uris": ["https://analytics.acme.com/callback"]}'
Response
{
  "id": "7f2c…",
  "name": "Acme Analytics",
  "client_id": "sov_3f9a1c0b7e2d4a6f8c1e",
  "redirect_uris": [
    "https://analytics.acme.com/callback"
  ],
  "scopes": [
    "openid",
    "profile",
    "email"
  ],
  "enabled": true,
  "created_at": "2026-10-18T10:20:00+00:00",
  "client_secret_set": true,
  "client_secret": "q3Zr…(43 characters)"
}

PATCH/api/v1/auth/oauth-apps/{app_id}

Change an OAuth app.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/oauth-apps/{app_id}

Path parameters

app_idstringrequired
The app's id.

Body

namestring | nulloptional
Name.
1–128 characters
redirect_urisarray<string> | nulloptional
Redirect URIs.
1–… items
scopesarray<string> | nulloptional
Scopes.
enabledboolean | nulloptional
On or off.

Responses

  • 200The app (without the secret).
  • 400Nothing to change, or an invalid value.
  • 404No such app.
  • 401No valid session or key.
  • 422The body is missing a required field or has a field of the wrong type.
curl -s -X PATCH "$URL/api/v1/auth/oauth-apps/7f2c…" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"enabled": false}'

DELETE/api/v1/auth/oauth-apps/{app_id}

Remove an OAuth app; its tokens stop working.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/oauth-apps/{app_id}

Path parameters

app_idstringrequired
The app.

Responses

  • 204Success, with no body.
  • 404No such app.
  • 401No valid session or key.
curl -s -X DELETE "$URL/api/v1/auth/oauth-apps/7f2c…" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Token keys

GET/api/v1/auth/project-jwks

The public keys that sign the project's app-user tokens, to verify them in your own backend.

A JSON Web Key Set (ES256). Cache it; keys rotate with overlap, so look up the token's kid (Sessions and tokens).

Who
Anyone: no key or session is needed.
Also at
/auth/v1/.well-known/jwks.json, /auth/v1/project-jwks

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/project-jwks" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "keys": [
    {
      "kty": "EC",
      "crv": "P-256",
      "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
      "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0",
      "kid": "p.f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07.7c1e9a04",
      "alg": "ES256",
      "use": "sig"
    }
  ]
}

Console sign-in (SAML, GitHub, Google, Microsoft, CLI)

These sign people in to the Sovaria console itself, not to your app.

GET/api/v1/auth/saml/login

Start SAML single sign-on into the Sovaria console for an organization (redirects to your identity provider).

For console members of organizations with SAML set up (Team plan and above). The browser is redirected (302) to the identity provider; after sign-in it comes back to the console signed in.

Who
Anyone: no key or session is needed.
Also at
/auth/v1/saml/login

Query parameters

org_idstringrequired
The organization.

Responses

  • 200302 redirect to the identity provider.
  • 400SAML isn't enabled or has no IdP SSO URL.
  • 403SAML SSO requires the Team plan or higher.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/saml/login?org_id=b2f4a1c8-7d3e-4f0a-9c6b-5e8d2a1f3c90" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

GET/api/v1/auth/saml/metadata/{org_id}

The organization's SAML service-provider metadata (XML), to load into your identity provider.

Who
Anyone: no key or session is needed.
Also at
/auth/v1/saml/metadata/{org_id}

Path parameters

org_idstringrequired
The organization.

Responses

  • 200application/samlmetadata+xml with the entity id and the ACS URL …/api/v1/auth/saml/acs.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/saml/metadata/b2f4a1c8-7d3e-4f0a-9c6b-5e8d2a1f3c90" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

GET/api/v1/auth/github/login

Sign in to the Sovaria console with GitHub, or connect GitHub to a project (browser redirect).

Redirects to GitHub. Afterwards the browser returns to redirect_uri with ?handoff=<one-time token> (valid 60 s), which the page exchanges with POST /api/v1/auth/oauth/handoff. With project_id it connects GitHub to that project; access=repo also allows writing to repositories.

Who
Anyone: no key or session is needed.
Also at
/auth/v1/github/login

Query parameters

redirect_uristringrequired
Where to return (a Sovaria address).
project_idstring | nulloptional
Connect GitHub to this project instead of signing in.
accessstring | nulloptional
repo for repository write access.

Responses

  • 200302 redirect to GitHub.
  • 400redirect_uri isn't allowed.
curl -s "$URL/api/v1/auth/github/login?redirect_uri=https://console.sovaria.in/auth/callback" \
  -H "apikey: $ANON_KEY"

GET/api/v1/auth/google/login

Sign in to the Sovaria console with Google (browser redirect).

Returns to redirect_uri with ?handoff=<one-time token> to exchange with POST /api/v1/auth/oauth/handoff.

Who
Anyone: no key or session is needed.
Also at
/auth/v1/google/login

Query parameters

redirect_uristringrequired
Where to return (a Sovaria address).

Responses

  • 200302 redirect to Google.
  • 400redirect_uri isn't allowed.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/google/login?redirect_uri=https://console.sovaria.in/auth/callback" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

GET/api/v1/auth/microsoft/login

Sign in to the Sovaria console with Microsoft (browser redirect).

Returns to redirect_uri with ?handoff=<one-time token> to exchange with POST /api/v1/auth/oauth/handoff.

Who
Anyone: no key or session is needed.
Also at
/auth/v1/microsoft/login

Query parameters

redirect_uristringrequired
Where to return (a Sovaria address).

Responses

  • 200302 redirect to Microsoft.
  • 400redirect_uri isn't allowed.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/microsoft/login?redirect_uri=https://console.sovaria.in/auth/callback" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

GET/api/v1/auth/github/connection-state

Whether GitHub is connected (for your account, or a project).

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/github/connection-state

Query parameters

project_idstring | nulloptional
A project; your account by default.

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/github/connection-state" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "provider": "github",
  "project_scope": "f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07",
  "connected": true,
  "connected_at": "2026-10-04T15:20:00Z",
  "updated_at": "2026-10-04T15:20:00Z",
  "token_hint": "gho_…1f",
  "metadata": {
    "email": "dev@acme.com",
    "login": "acme-dev",
    "name": "Acme Dev",
    "scopes": [
      "read:user",
      "repo",
      "user:email"
    ]
  }
}

DELETE/api/v1/auth/github/connection-state

Disconnect GitHub.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/github/connection-state

Query parameters

project_idstring | nulloptional
A project; your account by default.

Responses

  • 200Success.
  • 401No valid session or key.
curl -s -X DELETE "$URL/api/v1/auth/github/connection-state" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "provider": "github",
  "project_scope": "f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07",
  "connected": false,
  "disconnected": true
}

POST/api/v1/auth/cli/oauth/authorize

Approve a sov login request from the browser (the console's CLI consent page calls this).

The signed-in console user approves the CLI's PKCE request; the answer is the CLI's loopback URL with a one-time code and the state, which the CLI exchanges at POST /oauth/token.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/cli/oauth/authorize

Body

client_idstringrequired
The CLI's client id.
at least 1 characters
redirect_uristringrequired
The CLI's loopback URL.
at least 1 characters
code_challengestringrequired
PKCE challenge.
at least 1 characters
code_challenge_methodstringoptionaldefault S256
S256.
statestringoptional
The CLI's state value.

Returns

redirect_urlstringrequired

Responses

  • 200Success.
  • 401No valid session or key.
  • 422The body is missing a required field or has a field of the wrong type.
curl -s -X POST "$URL/api/v1/auth/cli/oauth/authorize" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"client_id": "sovaria-cli", "redirect_uri": "http://127.0.0.1:54321/callback", "code_challenge": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", "state": "af0ifjsldkj"}'
Response
{
  "redirect_url": "http://127.0.0.1:54321/callback?code=4f1c…&state=af0ifjsldkj"
}

Older paths

These answer the same as the project-scoped paths above; new code should use those.

GET/api/v1/auth/product-users

List the project's app users, newest first — the same as /api/v1/projects/{project_id}/auth/users, with the project taken from the project URL or the X-Sovaria-Project-ID header.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/product-users

Query parameters

limitintegeroptionaldefault 500
Users per page (1–500).
1–500
cursorstring | nulloptional
From the previous page's X-Next-Cursor.
0–200 characters
qstring | nulloptional
Part of an email, or an exact user id.
0–200 characters
statusstring | nulloptional
active or banned.
pattern `^(active|banned) HTTP API: users and auth settings | Sovaria Docs

Returns

node_idstring
The user's id — the one every other call takes as user_id, and auth.uid() in SQL.
idstring
The id of their sign-in record (rarely needed).
emailstring
is_activeboolean
false while banned.
created_at, updated_at, last_sign_in_atstring | null
ISO 8601.
mfa_enabled, phone_mfa_enabled, any_mfa_enabledboolean
Authenticator app, SMS, either.
rolesarray<string>
Their app roles in this project (Users and app roles).
role_sourcesarray<string>
Where the roles come from.
project_idstring

Responses

  • 200Success.
  • 403Only organization owners and admins can list app users.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/product-users?limit=50&status=active" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
[
  {
    "id": "a71d0c5e-2f84-4b9a-9e13-6c5d8f2b1a47",
    "node_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
    "email": "ana@example.com",
    "is_active": true,
    "created_at": "2026-10-02T08:14:22.481Z",
    "updated_at": "2026-10-16T19:03:51.112Z",
    "mfa_enabled": false,
    "phone_mfa_enabled": false,
    "any_mfa_enabled": false,
    "last_sign_in_at": "2026-10-17T09:41:05.330Z",
    "roles": [
      "staff"
    ],
    "role_sources": [
      "project_settings"
    ],
    "project_id": "f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07"
  }
]

POST/api/v1/auth/product-users

Create an app user (as an admin) — the same as /api/v1/projects/{project_id}/auth/users, with the project taken from the project URL or the X-Sovaria-Project-ID header.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/product-users

Body

namestringrequired
Display name.
1–255 characters
emailstringrequired
Sign-in email.
passwordstring | nulloptional
Optional; at least 8 characters.
at least 8 characters
rolesarray<string>optional
App roles to give them.
send_magic_linkbooleanoptionaldefault false
Email a sign-in link now.
email_confirmbooleanoptionaldefault false
Treat the email as confirmed.
env_idstring | nulloptional
An environment of the project.

Returns

node_idstring
The user's id — the one every other call takes as user_id, and auth.uid() in SQL.
idstring
The id of their sign-in record (rarely needed).
emailstring
is_activeboolean
false while banned.
created_at, updated_at, last_sign_in_atstring | null
ISO 8601.
mfa_enabled, phone_mfa_enabled, any_mfa_enabledboolean
Authenticator app, SMS, either.
rolesarray<string>
Their app roles in this project (Users and app roles).
role_sourcesarray<string>
Where the roles come from.
project_idstring
pending_inviteboolean
A sign-in link was sent and the email isn't confirmed yet.

Responses

  • 201Created.
  • 409That email is already registered in this project.
  • 422UNKNOWN_APP_ROLE — a role isn't defined; the body lists roles.
  • 403Only organization owners and admins.
  • 429Too many sign-in links for this address or IP.
  • 401No valid session or key.
curl -s -X POST "$URL/api/v1/auth/product-users" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name": "Ana Lima", "email": "ana@example.com", "roles": ["staff"], "send_magic_link": true}'
Response
{
  "id": "a71d0c5e-2f84-4b9a-9e13-6c5d8f2b1a47",
  "node_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "email": "ana@example.com",
  "is_active": true,
  "created_at": "2026-10-02T08:14:22.481Z",
  "updated_at": "2026-10-16T19:03:51.112Z",
  "mfa_enabled": false,
  "phone_mfa_enabled": false,
  "any_mfa_enabled": false,
  "last_sign_in_at": null,
  "roles": [
    "staff"
  ],
  "role_sources": [
    "project_settings"
  ],
  "project_id": "f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07",
  "actor_lane": "product",
  "auth_subject_type": "product_user",
  "pending_invite": true
}

DELETE/api/v1/auth/product-users/{user_id}

Delete an app user permanently and sign them out everywhere — the same as /api/v1/projects/{project_id}/auth/users/{user_id}, with the project taken from the project URL or the X-Sovaria-Project-ID header.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/product-users/{user_id}

Path parameters

user_idstringrequired
The user's id (node_id).

Responses

  • 204Success, with no body.
  • 404No such user in this project.
  • 403Only organization owners and admins.
  • 401No valid session or key.
curl -s -X DELETE "$URL/api/v1/auth/product-users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

POST/api/v1/auth/product-users/{user_id}/ban

Ban an app user, or unban them — each call switches it — the same as /api/v1/projects/{project_id}/auth/users/{user_id}/ban, with the project taken from the project URL or the X-Sovaria-Project-ID header.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/product-users/{user_id}/ban

Path parameters

user_idstringrequired
The user.

Responses

  • 200Success.
  • 400You can't ban yourself.
  • 404No such user.
  • 401No valid session or key.
curl -s -X POST "$URL/api/v1/auth/product-users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/ban" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "is_active": false
}

GET/api/v1/auth/product-users/{user_id}/roles

A user's app roles — the same as /api/v1/projects/{project_id}/auth/users/{user_id}/roles, with the project taken from the project URL or the X-Sovaria-Project-ID header.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/product-users/{user_id}/roles

Path parameters

user_idstringrequired
The user.

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/product-users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/roles" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "org_id": "b2f4a1c8-7d3e-4f0a-9c6b-5e8d2a1f3c90",
  "project_id": "f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07",
  "roles": [
    "staff"
  ]
}

PUT/api/v1/auth/product-users/{user_id}/roles

Replace a user's app roles — the same as /api/v1/projects/{project_id}/auth/users/{user_id}/roles, with the project taken from the project URL or the X-Sovaria-Project-ID header.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/product-users/{user_id}/roles

Path parameters

user_idstringrequired
The user.

Body

rolesarray<string>optional
The full list; [] removes all.

Responses

  • 200Success.
  • 422UNKNOWN_APP_ROLE — a role isn't defined in the project.
  • 401No valid session or key.
curl -s -X PUT "$URL/api/v1/auth/product-users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/roles" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"roles": ["staff", "billing"]}'
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "org_id": "b2f4a1c8-7d3e-4f0a-9c6b-5e8d2a1f3c90",
  "project_id": "f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07",
  "roles": [
    "billing",
    "staff"
  ]
}

GET/api/v1/auth/product-users/{user_id}/mfa

Whether a user has two-factor sign-in turned on — the same as /api/v1/projects/{project_id}/auth/users/{user_id}/mfa, with the project taken from the project URL or the X-Sovaria-Project-ID header.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/product-users/{user_id}/mfa

Path parameters

user_idstringrequired
The user.

Returns

mfa_enabledboolean
Authenticator app.
phone_mfa_enabledboolean
SMS codes.
totp_enrolledboolean
An authenticator secret is stored.
any_mfa_enabledboolean

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/product-users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/mfa" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "mfa_enabled": true,
  "phone_mfa_enabled": false,
  "totp_enrolled": true,
  "any_mfa_enabled": true
}

POST/api/v1/auth/product-users/{user_id}/mfa/reset

Turn off a user's two-factor sign-in (they lost their phone) — the same as /api/v1/projects/{project_id}/auth/users/{user_id}/mfa/reset, with the project taken from the project URL or the X-Sovaria-Project-ID header.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/product-users/{user_id}/mfa/reset

Path parameters

user_idstringrequired
The user.

Responses

  • 200Success.
  • 404No such user in this project.
  • 401No valid session or key.
curl -s -X POST "$URL/api/v1/auth/product-users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/mfa/reset" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "status": "reset",
  "message": "MFA has been reset for this user",
  "mfa_enabled": false,
  "phone_mfa_enabled": false,
  "totp_enrolled": false,
  "any_mfa_enabled": false
}

GET/api/v1/auth/users

Older listing of app users — use GET /api/v1/projects/{project_id}/auth/users.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/users

Query parameters

limitintegeroptionaldefault 500
Users per page (1–500).
1–500
cursorstring | nulloptional
From the previous page's X-Next-Cursor.
0–200 characters
qstring | nulloptional
Part of an email, or an exact user id.
0–200 characters
statusstring | nulloptional
active or banned.
pattern `^(active|banned) HTTP API: users and auth settings | Sovaria Docs

Returns

node_idstring
The user's id — the one every other call takes as user_id, and auth.uid() in SQL.
idstring
The id of their sign-in record (rarely needed).
emailstring
is_activeboolean
false while banned.
created_at, updated_at, last_sign_in_atstring | null
ISO 8601.
mfa_enabled, phone_mfa_enabled, any_mfa_enabledboolean
Authenticator app, SMS, either.
rolesarray<string>
Their app roles in this project (Users and app roles).
role_sourcesarray<string>
Where the roles come from.
project_idstring

Responses

  • 200Success.
  • 403Only organization owners and admins can list app users.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/users?limit=50&status=active" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
[
  {
    "id": "a71d0c5e-2f84-4b9a-9e13-6c5d8f2b1a47",
    "node_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
    "email": "ana@example.com",
    "is_active": true,
    "created_at": "2026-10-02T08:14:22.481Z",
    "updated_at": "2026-10-16T19:03:51.112Z",
    "mfa_enabled": false,
    "phone_mfa_enabled": false,
    "any_mfa_enabled": false,
    "last_sign_in_at": "2026-10-17T09:41:05.330Z",
    "roles": [
      "staff"
    ],
    "role_sources": [
      "project_settings"
    ],
    "project_id": "f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07"
  }
]

DELETE/api/v1/auth/users/{user_id}

Delete an app user permanently and sign them out everywhere — the same as /api/v1/projects/{project_id}/auth/users/{user_id}, with the project taken from the project URL or the X-Sovaria-Project-ID header.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/users/{user_id}

Path parameters

user_idstringrequired
The user's id (node_id).

Responses

  • 204Success, with no body.
  • 404No such user in this project.
  • 403Only organization owners and admins.
  • 401No valid session or key.
curl -s -X DELETE "$URL/api/v1/auth/users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

POST/api/v1/auth/users/{user_id}/ban

Ban an app user, or unban them — each call switches it — the same as /api/v1/projects/{project_id}/auth/users/{user_id}/ban, with the project taken from the project URL or the X-Sovaria-Project-ID header.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/users/{user_id}/ban

Path parameters

user_idstringrequired
The user.

Responses

  • 200Success.
  • 400You can't ban yourself.
  • 404No such user.
  • 401No valid session or key.
curl -s -X POST "$URL/api/v1/auth/users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/ban" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "is_active": false
}

GET/api/v1/auth/users/{user_id}/roles

A user's app roles — the same as /api/v1/projects/{project_id}/auth/users/{user_id}/roles, with the project taken from the project URL or the X-Sovaria-Project-ID header.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/users/{user_id}/roles

Path parameters

user_idstringrequired
The user.

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/roles" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "org_id": "b2f4a1c8-7d3e-4f0a-9c6b-5e8d2a1f3c90",
  "project_id": "f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07",
  "roles": [
    "staff"
  ]
}

PUT/api/v1/auth/users/{user_id}/roles

Replace a user's app roles — the same as /api/v1/projects/{project_id}/auth/users/{user_id}/roles, with the project taken from the project URL or the X-Sovaria-Project-ID header.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/users/{user_id}/roles

Path parameters

user_idstringrequired
The user.

Body

rolesarray<string>optional
The full list; [] removes all.

Responses

  • 200Success.
  • 422UNKNOWN_APP_ROLE — a role isn't defined in the project.
  • 401No valid session or key.
curl -s -X PUT "$URL/api/v1/auth/users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/roles" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"roles": ["staff", "billing"]}'
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "org_id": "b2f4a1c8-7d3e-4f0a-9c6b-5e8d2a1f3c90",
  "project_id": "f0c3e9a2-1b4d-4e7f-8a6c-2d9b5e3f1a07",
  "roles": [
    "billing",
    "staff"
  ]
}

GET/api/v1/auth/users/{user_id}/mfa

Whether a user has two-factor sign-in turned on — the same as /api/v1/projects/{project_id}/auth/users/{user_id}/mfa, with the project taken from the project URL or the X-Sovaria-Project-ID header.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/users/{user_id}/mfa

Path parameters

user_idstringrequired
The user.

Returns

mfa_enabledboolean
Authenticator app.
phone_mfa_enabledboolean
SMS codes.
totp_enrolledboolean
An authenticator secret is stored.
any_mfa_enabledboolean

Responses

  • 200Success.
  • 401No valid session or key.
curl -s "$URL/api/v1/auth/users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/mfa" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "mfa_enabled": true,
  "phone_mfa_enabled": false,
  "totp_enrolled": true,
  "any_mfa_enabled": true
}

POST/api/v1/auth/users/{user_id}/mfa/reset

Turn off a user's two-factor sign-in (they lost their phone) — the same as /api/v1/projects/{project_id}/auth/users/{user_id}/mfa/reset, with the project taken from the project URL or the X-Sovaria-Project-ID header.

Who
A project team member's session (the console, or a token from sov login) as Authorization: Bearer <token>. The member's role must allow the action.
Also at
/auth/v1/users/{user_id}/mfa/reset

Path parameters

user_idstringrequired
The user.

Responses

  • 200Success.
  • 404No such user in this project.
  • 401No valid session or key.
curl -s -X POST "$URL/api/v1/auth/users/3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93/mfa/reset" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
Response
{
  "user_id": "3c8f1e2a-9b47-4d6e-a5c1-7f0e2d4b8a93",
  "status": "reset",
  "message": "MFA has been reset for this user",
  "mfa_enabled": false,
  "phone_mfa_enabled": false,
  "totp_enrolled": false,
  "any_mfa_enabled": false
}
Something unclear or missing on this page?Tell us