Appearance
auth
13 endpoints.
POST /api/v1/auth/send-verification
Send Verification
Admin-triggered: send a verification email for a specific user. Returns success/failure so the grid can show a toast.
Request body
json
{
"properties": {
"user_id": {
"type": "integer",
"title": "User Id"
}
},
"type": "object",
"required": [
"user_id"
],
"title": "SendVerificationRequest"
}200 — Successful Response
json
{
"properties": {
"detail": {
"type": "string",
"title": "Detail"
}
},
"type": "object",
"required": [
"detail"
],
"title": "MessageResponse"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}POST /api/v1/auth/resend-verification
Resend Verification
Self-serve resend for a user blocked at login by the email-verification gate (login's email_unverified 403) — no auth, since they can't sign in yet. Always returns the same generic message regardless of whether the email is registered/verified, mirroring forgot_password's no-enumeration behaviour.
Request body
json
{
"properties": {
"email": {
"type": "string",
"title": "Email"
}
},
"type": "object",
"required": [
"email"
],
"title": "ResendVerificationRequest"
}200 — Successful Response
json
{
"properties": {
"detail": {
"type": "string",
"title": "Detail"
}
},
"type": "object",
"required": [
"detail"
],
"title": "MessageResponse"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}GET /api/v1/auth/verify-email
Verify Email
Click-through from the verification email link. Marks the user's email as verified and shows a simple success page the user can close. The token is single-use — subsequent clicks are rejected.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
token | query | yes | string |
200 — Successful Response
json
{}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}POST /api/v1/auth/signup
Signup
Request body
json
{
"properties": {
"email": {
"type": "string",
"format": "email",
"title": "Email"
},
"password": {
"type": "string",
"maxLength": 128,
"minLength": 8,
"title": "Password"
},
"full_name": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"title": "Full Name"
},
"org_name": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"title": "Org Name"
},
"org_slug": {
"type": "string",
"maxLength": 100,
"minLength": 2,
"title": "Org Slug"
}
},
"type": "object",
"required": [
"email",
"password",
"full_name",
"org_name",
"org_slug"
],
"title": "SignupRequest",
"description": "Self-serve signup: creates the user, their first organization, and an\nowner membership in one step."
}201 — Successful Response
json
{
"properties": {
"access_token": {
"type": "string",
"title": "Access Token"
},
"refresh_token": {
"type": "string",
"title": "Refresh Token"
},
"token_type": {
"type": "string",
"title": "Token Type",
"default": "bearer"
}
},
"type": "object",
"required": [
"access_token",
"refresh_token"
],
"title": "TokenResponse"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}POST /api/v1/auth/login
Login
Request body
json
{
"properties": {
"email": {
"type": "string",
"format": "email",
"title": "Email"
},
"password": {
"type": "string",
"title": "Password"
}
},
"type": "object",
"required": [
"email",
"password"
],
"title": "LoginRequest"
}200 — Successful Response
json
{
"properties": {
"access_token": {
"type": "string",
"title": "Access Token"
},
"refresh_token": {
"type": "string",
"title": "Refresh Token"
},
"token_type": {
"type": "string",
"title": "Token Type",
"default": "bearer"
}
},
"type": "object",
"required": [
"access_token",
"refresh_token"
],
"title": "TokenResponse"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}POST /api/v1/auth/forgot-password
Forgot Password
Always returns the same generic message, whether or not the email is registered, so this endpoint can't be used to enumerate accounts.
Request body
json
{
"properties": {
"email": {
"type": "string",
"format": "email",
"title": "Email"
}
},
"type": "object",
"required": [
"email"
],
"title": "ForgotPasswordRequest"
}200 — Successful Response
json
{
"properties": {
"detail": {
"type": "string",
"title": "Detail"
}
},
"type": "object",
"required": [
"detail"
],
"title": "MessageResponse"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}POST /api/v1/auth/reset-password
Reset Password
Request body
json
{
"properties": {
"token": {
"type": "string",
"title": "Token"
},
"password": {
"type": "string",
"maxLength": 128,
"minLength": 8,
"title": "Password"
}
},
"type": "object",
"required": [
"token",
"password"
],
"title": "ResetPasswordRequest"
}200 — Successful Response
json
{
"properties": {
"detail": {
"type": "string",
"title": "Detail"
}
},
"type": "object",
"required": [
"detail"
],
"title": "MessageResponse"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}POST /api/v1/auth/refresh
Refresh
Request body
json
{
"properties": {
"refresh_token": {
"type": "string",
"title": "Refresh Token"
}
},
"type": "object",
"required": [
"refresh_token"
],
"title": "TokenRefresh"
}200 — Successful Response
json
{
"properties": {
"access_token": {
"type": "string",
"title": "Access Token"
},
"refresh_token": {
"type": "string",
"title": "Refresh Token"
},
"token_type": {
"type": "string",
"title": "Token Type",
"default": "bearer"
}
},
"type": "object",
"required": [
"access_token",
"refresh_token"
],
"title": "TokenResponse"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}POST /api/v1/auth/impersonate
Impersonate
Exchange a one-time impersonation code — minted by a staff superuser's "Log in as" button (see app.services.impersonation) — for portal session tokens. The code IS the authorization: single-use, short-lived, and only ever issued by a superuser, so this endpoint is intentionally unauthenticated (like accepting an invite). Consumed on first use; rejected if used/expired.
Request body
json
{
"properties": {
"code": {
"type": "string",
"title": "Code"
}
},
"type": "object",
"required": [
"code"
],
"title": "_ImpersonateRequest"
}200 — Successful Response
json
{
"properties": {
"access_token": {
"type": "string",
"title": "Access Token"
},
"refresh_token": {
"type": "string",
"title": "Refresh Token"
},
"token_type": {
"type": "string",
"title": "Token Type",
"default": "bearer"
}
},
"type": "object",
"required": [
"access_token",
"refresh_token"
],
"title": "TokenResponse"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}GET /api/v1/auth/me
Me
Return the user plus the tenants for their switcher.
Superusers can act on ANY tenant (see get_tenant_context), so we return every active org for them (role "owner") — otherwise a platform admin with no membership would have an empty switcher and couldn't scope any request. Regular users get exactly the tenants they hold an active membership in.
200 — Successful Response
json
{
"properties": {
"id": {
"type": "string",
"title": "Id"
},
"email": {
"type": "string",
"title": "Email"
},
"full_name": {
"type": "string",
"title": "Full Name"
},
"is_superuser": {
"type": "boolean",
"title": "Is Superuser"
},
"management_read_only": {
"type": "boolean",
"title": "Management Read Only",
"default": false
},
"customer_facing": {
"type": "boolean",
"title": "Customer Facing",
"default": false
},
"is_reseller": {
"type": "boolean",
"title": "Is Reseller",
"default": false
},
"is_reseller_user": {
"type": "boolean",
"title": "Is Reseller User",
"default": false
},
"tenants": {
"items": {
"properties": {
"id": {
"type": "string",
"title": "Id"
},
"name": {
"type": "string",
"title": "Name"
},
"slug": {
"type": "string",
"title": "Slug"
},
"plan_tier": {
"type": "string",
"title": "Plan Tier"
},
"status": {
"type": "string",
"title": "Status"
},
"role": {
"type": "string",
"title": "Role"
},
"icon_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Icon Url"
},
"domain_name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Domain Name"
},
"domain_names": {
"items": {
"type": "string"
},
"type": "array",
"title": "Domain Names",
"default": []
},
"parent_tenant_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Parent Tenant Id"
},
"is_reseller": {
"type": "boolean",
"title": "Is Reseller",
"default": false
}
},
"type": "object",
"required": [
"id",
"name",
"slug",
"plan_tier",
"status",
"role"
],
"title": "TenantSummary",
"description": "A tenant the current user belongs to, plus their role in it."
},
"type": "array",
"title": "Tenants"
}
},
"type": "object",
"required": [
"id",
"email",
"full_name",
"is_superuser",
"tenants"
],
"title": "MeResponse"
}PUT /api/v1/auth/me
Update Me
Self-service profile update — change own name, email and/or password. Bumps token_version when the password changes so existing sessions are revoked (same mechanism as the reset-password flow).
Request body
json
{
"properties": {
"full_name": {
"anyOf": [
{
"type": "string",
"maxLength": 255,
"minLength": 1
},
{
"type": "null"
}
],
"title": "Full Name"
},
"email": {
"anyOf": [
{
"type": "string",
"format": "email"
},
{
"type": "null"
}
],
"title": "Email"
},
"password": {
"anyOf": [
{
"type": "string",
"maxLength": 128,
"minLength": 8
},
{
"type": "null"
}
],
"title": "Password"
}
},
"type": "object",
"title": "UpdateProfileRequest",
"description": "Self-service profile edit — the user can change their own name, email,\nand/or password. Only supplied fields are updated (all optional)."
}200 — Successful Response
json
{
"properties": {
"id": {
"type": "string",
"title": "Id"
},
"email": {
"type": "string",
"title": "Email"
},
"full_name": {
"type": "string",
"title": "Full Name"
},
"is_superuser": {
"type": "boolean",
"title": "Is Superuser"
},
"management_read_only": {
"type": "boolean",
"title": "Management Read Only",
"default": false
},
"customer_facing": {
"type": "boolean",
"title": "Customer Facing",
"default": false
},
"is_reseller": {
"type": "boolean",
"title": "Is Reseller",
"default": false
},
"is_reseller_user": {
"type": "boolean",
"title": "Is Reseller User",
"default": false
},
"tenants": {
"items": {
"properties": {
"id": {
"type": "string",
"title": "Id"
},
"name": {
"type": "string",
"title": "Name"
},
"slug": {
"type": "string",
"title": "Slug"
},
"plan_tier": {
"type": "string",
"title": "Plan Tier"
},
"status": {
"type": "string",
"title": "Status"
},
"role": {
"type": "string",
"title": "Role"
},
"icon_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Icon Url"
},
"domain_name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Domain Name"
},
"domain_names": {
"items": {
"type": "string"
},
"type": "array",
"title": "Domain Names",
"default": []
},
"parent_tenant_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Parent Tenant Id"
},
"is_reseller": {
"type": "boolean",
"title": "Is Reseller",
"default": false
}
},
"type": "object",
"required": [
"id",
"name",
"slug",
"plan_tier",
"status",
"role"
],
"title": "TenantSummary",
"description": "A tenant the current user belongs to, plus their role in it."
},
"type": "array",
"title": "Tenants"
}
},
"type": "object",
"required": [
"id",
"email",
"full_name",
"is_superuser",
"tenants"
],
"title": "MeResponse"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}GET /api/v1/auth/me/notifications
Get Notification Preferences
No row yet ⇒ the dialog's own defaults (nothing persisted until Save).
200 — Successful Response
json
{
"properties": {
"low_balance_airtime_reminder": {
"type": "boolean",
"title": "Low Balance Airtime Reminder",
"default": true
},
"marketing_emails": {
"type": "boolean",
"title": "Marketing Emails",
"default": true
}
},
"type": "object",
"title": "NotificationPreferences",
"description": "My Notifications dialog state. Security emails (sign-in alerts) aren't\nhere — they're mandatory, no opt-out (see app.services.sign_in_alert)."
}PUT /api/v1/auth/me/notifications
Update Notification Preferences
Request body
json
{
"properties": {
"low_balance_airtime_reminder": {
"type": "boolean",
"title": "Low Balance Airtime Reminder",
"default": true
},
"marketing_emails": {
"type": "boolean",
"title": "Marketing Emails",
"default": true
}
},
"type": "object",
"title": "NotificationPreferences",
"description": "My Notifications dialog state. Security emails (sign-in alerts) aren't\nhere — they're mandatory, no opt-out (see app.services.sign_in_alert)."
}200 — Successful Response
json
{
"properties": {
"low_balance_airtime_reminder": {
"type": "boolean",
"title": "Low Balance Airtime Reminder",
"default": true
},
"marketing_emails": {
"type": "boolean",
"title": "Marketing Emails",
"default": true
}
},
"type": "object",
"title": "NotificationPreferences",
"description": "My Notifications dialog state. Security emails (sign-in alerts) aren't\nhere — they're mandatory, no opt-out (see app.services.sign_in_alert)."
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}