Last updated: June 9, 2026·31 min read·View source

User & Authentication API

Complete reference for authentication, profile, and user management endpoints.

OpenAPI tag groups: authentication, profile, users


Table of contents

  1. Overview
  2. User types & roles
  3. Authentication & authorization
  4. Authentication flow
  5. Common errors
  6. Field validation & constraints
  7. Query, filtering, sorting & pagination
  8. Authentication APIs (/api/v3/auth)
  9. Profile APIs (/api/v3/users/profile)
  10. User CRUD APIs (/api/v3/user/v1/users)
  11. Related events & side effects

Overview

Area Base path Authentication Authorization
Authentication /api/v3/auth Mixed — public or validated JWT Role checks on manager/staff endpoints only
Profile /api/v3/users/profile Validated JWT access token (all routes) Self only — authenticated user accesses own profile
User CRUD /api/v3/user/v1/users Bearer header on most routes; no JWT validation No role checks — list and health check are fully public

JWT details

  • Header: Authorization: Bearer <access_token>
  • Access token lifetime: 5 minutes (production), 24 hours when debug=true
  • Refresh token lifetime: 24 hours
  • Algorithm: configured via JWT_ALGORITHM (typically HS512)

User types & roles

Name user_type value Typical use
SYSTEM_MANAGER 100 Full admin
MANAGER 200 User management
OFFICE_EMPLOYEE 300 Office staff
WAREHOUSE_EMPLOYEE 400 Warehouse staff
CUSTOMER 1000 Self-registration default
CUSTOMS_OFFICER 2000 Customs role
UNDEFINED 9999 Fallback

Manager-only auth endpoints require user_type 100 or 200 unless noted otherwise.

Create user (POST /auth/user/create) permissions:

Caller role Can create
System Manager (100) Any role
Manager (200) Any except System Manager
Office Employee (300) Customer (1000) only

Authentication & authorization

This section documents who can call each endpoint — authentication (proving identity) and authorization (permission to perform the action).

Authentication mechanisms

Two different Bearer dependencies are used in this API:

Mechanism Used by Validates JWT Validates role Implementation
Validated JWT /api/v3/auth (protected routes), /api/v3/users/profile Yes — signature, expiry, token_type=access Per-endpoint JWTBearer (src/auth/bearer.py)
Bearer header only /api/v3/user/v1/users (most routes) No — only checks Authorization: Bearer … is present No OAuth2PasswordBearer (src/user/routers/user.py)
None Public auth routes, list users, health check No dependency

Security note: User CRUD routes accept any non-empty Bearer token string without verifying it. Do not rely on these routes for production authorization until JWT validation is added.

Required header (when authentication applies):

Authorization: Bearer <access_token>

Obtain <access_token> from POST /api/v3/auth/login. Use the access token, not the refresh token, for protected routes using validated JWT.

Account state required to obtain a token

Login (POST /api/v3/auth/login) succeeds only when all of the following are true:

Condition Failure
Email and password match 401Invalid credentials
is_active=true 401User account is inactive
is_banned=false 401User account is banned
email_verified=true 401Email not verified

Any role that passes login receives a JWT whose user_type claim reflects their role.

Role groups (authorization shorthand)

Shorthand user_type values Roles
Public Unauthenticated callers
Any authenticated 100, 200, 300, 400, 1000, 2000, 9999 All roles with a valid access token
Manager+ 100, 200 System Manager, Manager
Staff+ 100, 200, 300 System Manager, Manager, Office Employee
Self Any authenticated Caller may only act on their own account (JWT user_id)

Roles not listed on a restricted endpoint receive 403 Forbidden.

Operation types

Operation Meaning in this API
Execute Trigger a one-off action (login, logout, send activation code, log login event)
Read Retrieve data without modification
Create Insert a new user record
Update Modify existing user or profile fields
Delete Soft-delete (deactivate) or hard-delete a user
Manage Administrative action on another user's account (ban, activate, set password, verify email)

Master access control matrix

Complete reference for all endpoints. Auth = authentication requirement; Authz = authorization (role/scope).

# Method Path Auth Authz / allowed roles Operations
1 POST /api/v3/auth/login Public Any caller Execute
2 POST /api/v3/auth/register Public Any caller; creates Customer (1000) only Create
3 POST /api/v3/auth/token/refresh Public Any caller with valid refresh token Execute
4 POST /api/v3/auth/token/verify Public Any caller with token to verify Execute
5 POST /api/v3/auth/logout Validated JWT Any authenticated Execute (self)
6 POST /api/v3/auth/password/change Validated JWT Self — changes own password Update (self)
7 POST /api/v3/auth/password/set Validated JWT Manager+ (100, 200) Manage
8 POST /api/v3/auth/password/reset Public Any caller; target email must exist & be verified Execute
9 POST /api/v3/auth/password/reset/confirm Public Any caller with valid reset code Update
10 POST /api/v3/auth/activation/send Public Any caller; target email must exist Execute
11 POST /api/v3/auth/activation/confirm Public Any caller with valid activation code Update
12 POST /api/v3/auth/user/activate Validated JWT Manager+ (100, 200) Manage
13 POST /api/v3/auth/user/ban Validated JWT Manager+ (100, 200) Manage
14 POST /api/v3/auth/user/create Validated JWT Staff+ with role-specific create limits (see below) Create
15 GET /api/v3/users/profile/ Validated JWT Self — own profile Read (self)
16 PATCH /api/v3/users/profile/ Validated JWT Self — own profile Update (self)
17 GET /api/v3/users/profile/me Validated JWT Self — own detailed profile Read (self)
18 POST /api/v3/users/profile/avatar Validated JWT Self (501 not implemented) Update (self)
19 POST /api/v3/user/v1/users/ Bearer header No role check — any Bearer string Create
20 POST /api/v3/user/v1/users/bulk Bearer header No role check Create
21 GET /api/v3/user/v1/users/{user_id} Bearer header No role check Read
22 GET /api/v3/user/v1/users/email/{email} Bearer header No role check Read
23 GET /api/v3/user/v1/users/mobile/{mobile} Bearer header No role check Read
24 GET /api/v3/user/v1/users/ Public Any caller Read (list)
25 PUT /api/v3/user/v1/users/{user_id} Bearer header No role check Update
26 PATCH /api/v3/user/v1/users/{user_id} Bearer header No role check Update
27 PUT /api/v3/user/v1/users/bulk Bearer header No role check Update
28 DELETE /api/v3/user/v1/users/{user_id} Bearer header No role check Delete (soft)
29 DELETE /api/v3/user/v1/users/{user_id}/hard Bearer header No role check Delete (hard)
30 POST /api/v3/user/v1/users/{user_id}/verify-email Bearer header No role check Manage
31 POST /api/v3/user/v1/users/{user_id}/password Bearer header No role check Manage
32 POST /api/v3/user/v1/users/{user_id}/log-login Bearer header No role check Execute
33 POST /api/v3/user/v1/users/{user_id}/log-logout Bearer header No role check Execute
34 GET /api/v3/user/v1/users/test Public Any caller Execute (health)

Create user — role-specific authorization (`POST /api/v3/auth/user/create`)

Caller role Can create user_type Forbidden example
System Manager (100) 100, 200, 300, 400, 1000, 2000, 9999
Manager (200) 200, 300, 400, 1000, 2000, 9999 Creating System Manager (100) → 403 Managers cannot create system managers
Office Employee (300) 1000 (Customer) only Creating Manager (200) → 403 Office employees can only create customers
All other roles 403 Insufficient permissions

Authorization error responses

HTTP detail Typical cause
403 Invalid authorization code. Missing Authorization header (validated JWT routes)
403 Invalid authentication scheme. Header is not Bearer
403 Invalid token or expired token. Bad/expired access token (validated JWT routes)
403 Invalid token type. Access token required. Refresh token used where access token required
403 Only managers can set user passwords Non-manager calling POST /auth/password/set
403 Only managers can activate/deactivate users Non-manager calling POST /auth/user/activate
403 Only managers can ban/unban users Non-manager calling POST /auth/user/ban
403 Insufficient permissions Role below Staff+ calling POST /auth/user/create
403 Managers cannot create system managers Manager (200) creating user_type 100
403 Office employees can only create customers Office Employee (300) creating non-customer
403 (OAuth2) missing credentials User CRUD route called without Authorization header

Access scenario examples

Authorized

# System Manager sets another user's password
curl -X POST http://127.0.0.1:8000/api/v3/auth/password/set \
  -H "Authorization: Bearer <manager_access_token>" \
  -H "Content-Type: application/json" \
  -d '{"user": "550e8400-e29b-41d4-a716-446655440000", "password": "newpass12", "re_password": "newpass12"}'
# → 200 { "message": "Password set successfully" }
# Customer reads own profile
curl http://127.0.0.1:8000/api/v3/users/profile/ \
  -H "Authorization: Bearer <customer_access_token>"
# → 200 ProfileResponse (always the JWT holder's profile)
# Office Employee creates a customer
curl -X POST http://127.0.0.1:8000/api/v3/auth/user/create \
  -H "Authorization: Bearer <office_employee_access_token>" \
  -H "Content-Type: application/json" \
  -d '{"email": "new@example.com", "first_name": "Ann", "last_name": "Lee", "user_type": 1000}'
# → 201 RegisterResponse
# Unauthenticated user lists active users (public)
curl "http://127.0.0.1:8000/api/v3/user/v1/users/?limit=10"
# → 200 UserListResponse

Unauthorized

# Customer attempts to ban another user
curl -X POST http://127.0.0.1:8000/api/v3/auth/user/ban \
  -H "Authorization: Bearer <customer_access_token>" \
  -H "Content-Type: application/json" \
  -d '{"user": "550e8400-e29b-41d4-a716-446655440000", "status": true}'
# → 403 { "detail": "Only managers can ban/unban users" }
# Manager attempts to create a System Manager
curl -X POST http://127.0.0.1:8000/api/v3/auth/user/create \
  -H "Authorization: Bearer <manager_access_token>" \
  -H "Content-Type: application/json" \
  -d '{"email": "admin2@example.com", "first_name": "Bob", "last_name": "Admin", "user_type": 100}'
# → 403 { "detail": "Managers cannot create system managers" }
# Request profile without token
curl http://127.0.0.1:8000/api/v3/users/profile/
# → 403 { "detail": "Invalid authorization code." }
# Use refresh token instead of access token on a validated-JWT route
curl http://127.0.0.1:8000/api/v3/users/profile/ \
  -H "Authorization: Bearer <refresh_token>"
# → 403 { "detail": "Invalid token type. Access token required." }
# User CRUD route without Authorization header
curl http://127.0.0.1:8000/api/v3/user/v1/users/550e8400-e29b-41d4-a716-446655440000
# → 403 (OAuth2 — not authenticated)

Authentication flow

sequenceDiagram participant Client participant API participant DB Client->>API: POST /api/v3/auth/login {email, password} API->>DB: Verify user + password DB-->>API: User record API-->>Client: {access, refresh, exp_time, user_type} Client->>API: GET /api/v3/users/profile/ (Bearer access) API-->>Client: ProfileResponse Note over Client,API: When access expires Client->>API: POST /api/v3/auth/token/refresh {refresh} API-->>Client: {access}

Common errors

HTTP status codes

Code Meaning
200 Success
201 Created
204 Success, no body
400 Validation or business rule failure
401 Invalid credentials or token
403 Forbidden (role or bearer scheme)
404 Resource not found
422 Pydantic validation error (request body)
501 Not implemented
503 Database unavailable

Bearer middleware (`403`)

detail Cause
Invalid authorization code. Missing Authorization header
Invalid authentication scheme. Not Bearer
Invalid token or expired token. Bad or expired JWT
Invalid token type. Access token required. Refresh token used as access

Authorization failures (`403`)

Role or permission denied on protected endpoints. See Authorization error responses for the full list.

detail Endpoint(s) Who is denied
Only managers can set user passwords POST /auth/password/set All roles except 100, 200
Only managers can activate/deactivate users POST /auth/user/activate All roles except 100, 200
Only managers can ban/unban users POST /auth/user/ban All roles except 100, 200
Insufficient permissions POST /auth/user/create 400, 1000, 2000, 9999
Managers cannot create system managers POST /auth/user/create Manager (200) creating user_type 100
Office employees can only create customers POST /auth/user/create Office Employee (300) creating non-1000

Login failures (`401`)

detail Cause
Invalid credentials Wrong email/password or user not found
User account is inactive is_active=false
User account is banned is_banned=true
Email not verified email_verified=false

Pydantic validation errors (`422`)

When request body or query parameters fail schema validation, FastAPI returns 422 Unprocessable Entity:

{
  "detail": [
    {
      "type": "string_too_short",
      "loc": ["body", "password"],
      "msg": "String should have at least 8 characters",
      "input": "short",
      "ctx": { "min_length": 8 }
    }
  ]
}

Each item in detail includes loc (field path), msg, type, and often ctx with min_length, max_length, or other bounds.


Field validation & constraints

Constraints come from Pydantic request schemas (src/auth/schemas.py, src/user/schemas/user.py), database columns (src/user/models/user.py), and service-layer rules (src/auth/service.py).

Where a Pydantic schema does not set a bound, the database column limit still applies at persistence time.

Notation

Symbol Meaning
No min/max enforced at the documented layer
(DB) Enforced by PostgreSQL column definition
(service) Enforced in service layer after schema validation
(computed) Derived in response; not accepted in requests

Validation layers (in order):

  1. Pydantic / FastAPI Query422 on failure (body or query params)
  2. Service / business logic400 or 401 with detail message
  3. Database400 on unique violations; truncation or error on column overflow

String fields (request & path)

Field Endpoints Required Min length Max length Format / other constraints
email Login, register, reset, activation, manager create, user CRUD Varies 1 (EmailStr) 320 (EmailStr) Valid email (EmailStr / RFC 5321). Must be unique in core_user. Login lookup is case-insensitive (normalized to lowercase). User CRUD uses plain str with no Pydantic max (DB: unlimited TEXT).
password Login, register, change/set/reset password, user CRUD create Varies 8 (auth) / 1 (login, service) Plain text in request; stored as PBKDF2-SHA256 hash (pbkdf2_sha256$…). Login schema defaults to ""; service rejects empty/falsy password (401). User CRUD has no min/max in schema. No API max length.
re_password Register, change/set/reset password Where password is sent 8 Must exactly match password (Pydantic validator).
current_password Change password Yes No length bound in schema; verified against stored hash.
first_name Register, manager create, profile update, user CRUD Varies 1 150 Non-empty when required. DB column max_length=150.
last_name Register, manager create, profile update, user CRUD Varies 1 150 Non-empty when required. DB column max_length=150.
mobile Register, manager create, user CRUD No 11 Must be unique if provided. DB column max_length=11.
refresh Token refresh, logout Yes JWT refresh token string. No Pydantic min_length; empty string passes schema but fails JWT decode (401). Accepts alias refresh_token in request body.
token (JWT) Token verify Yes JWT string (access or refresh). No Pydantic length bound; invalid/empty fails decode (401).
avatar User CRUD update (response fields) No Optional URL/path string; no API-level max. Upload endpoint not implemented.
new_password POST …/users/{id}/password Yes (query) Passed as query parameter ?new_password=…; no schema validation in router. Stored as provided (should be pre-hashed by caller — see implementation note).
Path: email GET …/users/email/{email} URL-encoded email string; exact match against stored value (case-sensitive in CRUD).
Path: mobile GET …/users/mobile/{mobile} 1 11 Exact match; max 11 chars per DB.

String fields (response only)

Field Min length Max length Format / notes
access 1 JWT access token (~200–600 chars typical)
refresh 1 JWT refresh token
message 0 Human-readable status string
detail 1 Error or success detail (e.g. token verify)
full_name 1 301 (computed) first_name (≤150) + " " + last_name (≤150)
joined_at, login_at, logout_at, created_at, updated_at 19 19 Datetime string when non-null: YYYY.MM.DD HH:MM:SS
UUID fields in JSON (id, etc.) 36 36 Standard hyphenated UUID string

Numeric fields

Field Endpoints Type Min Max Other constraints
token (verification code) Activation confirm, password reset confirm integer 100000 999999 Exactly 6 digits. Generated via random.randint(100000, 999999). Valid for 900 seconds (15 min, EMAIL_TOKEN_EXPIRE). Values outside range pass Pydantic but fail service validation (400).
code (debug response) Activation/reset send integer 100000 999999 Returned only when debug=true.
user_type Login response, manager create, profile response integer 100 9999 Discrete allowed values only: 100, 200, 300, 400, 1000, 2000, 9999 — not a continuous range. Stored as PostgreSQL SmallInteger (-32768–32767). Invalid values may persist on User CRUD paths without enum check.
total List users response integer 0 Count of active users (is_active=true) regardless of active_only filter.
exp_time Login response integer 300 86400 Access-token TTL in seconds: 300 (5 min) in production; 86400 (24 h) when debug=true.
timeout Activation/reset send response integer 900 900 Token validity period in seconds (EMAIL_TOKEN_EXPIRE).
offset List users integer 0 ge=0. Number of records to skip.
limit List users integer 1 1000 ge=1, le=1000. Page size.
_version (internal, response via DB) integer 1 Optimistic versioning; incremented on each update.

Array fields

Field Endpoints Min items Max items Item constraints
users Bulk create, bulk update 1 Each element follows UserCreate / bulk-update item rules

Boolean fields

Field Endpoints Default Constraints
status Activate/deactivate, ban/unban user Required. true = activate/ban; false = deactivate/unban.
is_active Register (implicit), manager create, user CRUD, profile /me true DB boolean. Registration sets is_active=true but email_verified=false until activation.
two_step_auth Profile get/update false Optional on PATCH; toggles 2FA flag (implementation TBD).
notify_after_login Profile get/update false Optional on PATCH.
active_only List users (query) true When true, returns only is_active=true users.

UUID fields

Field Endpoints Format Constraints
id Responses, path {user_id} UUID v4 RFC 4122. Path parameter must be valid UUID or 422.
user Activate, ban, set password UUID Target user ID; must exist or 400 / 404.

Password & token rules (service layer)

Rule Detail
Password hashing PBKDF2-SHA256, 600,000 iterations, Django-compatible format.
Password min length 8 characters on all password-setting endpoints (Pydantic).
validate_password_strength() Exists in src/auth/security.py (rejects all-numeric and common passwords) but is not called by register/change endpoints today — only Pydantic min_length=8 applies.
Verification code range 100000–999999 (inclusive).
Verification code TTL 900 s (15 minutes).
Manager-generated password Random 8 characters (ascii_letters + digits) on POST /auth/user/create.
JWT access lifetime 300 s prod / 86400 s debug.
JWT refresh lifetime 86400 s (24 h).

Uniqueness constraints (database)

Column Constraint
email Unique index; duplicate returns 400 (Email already registered).
mobile Unique index (when not null); duplicate returns 400 (Mobile number already registered).

Fields with no API-level length validation

These fields have no Pydantic min/max on the User CRUD router but are bounded by the database or business logic:

Field DB / behavior limit
email (User CRUD) Optional[str], unique; no max_length on column — recommend ≤ 320
password (User CRUD create) Required string; no min/max in schema; not auto-hashed
first_name, last_name (User CRUD) DB max_length=150; no Pydantic min on create
avatar (User CRUD update) No column max defined
new_password (query param) No validation; any length accepted

Query, filtering, sorting & pagination

Summary by endpoint group

Capability Auth (/api/v3/auth) Profile (/api/v3/users/profile) User CRUD (/api/v3/user/v1/users)
Pagination Not supported Not supported List users only (offset, limit)
Filtering Not supported Not supported List users only (active_only)
Sorting Not supported Not supported Not supported (order clause commented out in CRUD)
Full-text search Not supported Not supported Not supported
Field-specific query filters Not supported Not supported Not supported (no email, user_type, etc. query params)

Per-endpoint query & list capabilities

Every endpoint in this document and whether it accepts query parameters, path filters, sorting, or search.

# Method Path Query params Sort Filter Search Pagination
1 POST /api/v3/auth/login
2 POST /api/v3/auth/register
3 POST /api/v3/auth/token/refresh
4 POST /api/v3/auth/token/verify
5 POST /api/v3/auth/logout
6 POST /api/v3/auth/password/change
7 POST /api/v3/auth/password/set
8 POST /api/v3/auth/password/reset
9 POST /api/v3/auth/password/reset/confirm
10 POST /api/v3/auth/activation/send
11 POST /api/v3/auth/activation/confirm
12 POST /api/v3/auth/user/activate
13 POST /api/v3/auth/user/ban
14 POST /api/v3/auth/user/create
15 GET /api/v3/users/profile/
16 PATCH /api/v3/users/profile/
17 GET /api/v3/users/profile/me
18 POST /api/v3/users/profile/avatar
19 POST /api/v3/user/v1/users/
20 POST /api/v3/user/v1/users/bulk
21 GET /api/v3/user/v1/users/{user_id} Path: UUID lookup
22 GET /api/v3/user/v1/users/email/{email} Path: exact email
23 GET /api/v3/user/v1/users/mobile/{mobile} Path: exact mobile
24 GET /api/v3/user/v1/users/ offset, limit, active_only active_only offset/limit
25 PUT /api/v3/user/v1/users/{user_id}
26 PATCH /api/v3/user/v1/users/{user_id}
27 PUT /api/v3/user/v1/users/bulk
28 DELETE /api/v3/user/v1/users/{user_id}
29 DELETE /api/v3/user/v1/users/{user_id}/hard
30 POST /api/v3/user/v1/users/{user_id}/verify-email
31 POST /api/v3/user/v1/users/{user_id}/password new_password
32 POST /api/v3/user/v1/users/{user_id}/log-login
33 POST /api/v3/user/v1/users/{user_id}/log-logout
34 GET /api/v3/user/v1/users/test

Lookup vs list: Endpoints 21–23 perform exact-match retrieval by path segment (not query-string filters). Endpoint 24 is the only paginated collection endpoint.

List users — `GET /api/v3/user/v1/users/`

The only endpoint in this section that supports query parameters.

Parameter Type Required Default Min Max Description
offset integer No 0 0 Records to skip (offset-based pagination).
limit integer No 100 1 1000 Maximum records per page.
active_only boolean No true true → only is_active=true; false → all users from view.

Pagination response fields

Field Type Description
users array Current page of UserResponse objects.
total integer Count of active users only (crud.count() filters is_active=true regardless of active_only).
offset integer Echo of request offset.
limit integer Echo of request limit.

Pagination example

# Page 1 — first 50 active users
curl "http://127.0.0.1:8000/api/v3/user/v1/users/?offset=0&limit=50&active_only=true"

# Page 2
curl "http://127.0.0.1:8000/api/v3/user/v1/users/?offset=50&limit=50&active_only=true"

# Include inactive users (still no sort order guaranteed)
curl "http://127.0.0.1:8000/api/v3/user/v1/users/?offset=0&limit=100&active_only=false"

Sorting: Not supported. list_active() and list_all() have commented-out .order_by(created_at.desc()) — result order is undefined (database-dependent).

Search: Not supported. No q, search, query, or full-text parameters.

Filter: Only active_only (boolean). No filters for user_type, is_banned, email_verified, email, mobile, name, or date ranges.

Pagination type: Offset-based (not cursor-based). There is no page parameter — compute offset = page_index × limit.

Rejected query values: offset < 0, limit < 1, or limit > 1000422 with Pydantic validation error.

Search / filter alternatives: No query parameters for email, name, user_type, is_banned, date ranges, or free-text search. Use dedicated lookup endpoints instead:

Lookup Endpoint
By ID GET /api/v3/user/v1/users/{user_id}
By email GET /api/v3/user/v1/users/email/{email}
By mobile GET /api/v3/user/v1/users/mobile/{mobile}

Update password query parameter

POST /api/v3/user/v1/users/{user_id}/password

Parameter Type Required Location Constraints
new_password string Yes Query string No min/max validation in router

Path-parameter lookups (no query string)

Endpoint Path param Constraints
GET …/users/{user_id} user_id Valid UUID
GET …/users/email/{email} email Full email as path segment; URL-encode special characters
GET …/users/mobile/{mobile} mobile Max 11 characters

Endpoints with no query/path parameters beyond auth

All /api/v3/auth endpoints (except path segments shown in URL) and all /api/v3/users/profile endpoints accept only JSON body fields or no parameters. No pagination, filtering, or sorting applies.


Authentication APIs (`/api/v3/auth`)

1. Login

Name User login
URL POST /api/v3/auth/login
Authentication None (public)
Authorization Any caller
Allowed roles — (unauthenticated)
Operations Execute — obtain JWT tokens

Request body (JSON)

Field Type Required Min length Max length Min value Max value Description
email string (email) Yes 1 320 Valid email format; lookup is case-insensitive
password string Yes 1 (service) Plain-text password; empty string rejected (401)

Response 200TokenResponse

Field Type Required Min length Max length Min value Max value Description
access string 1 JWT access token
refresh string 1 JWT refresh token
exp_time integer 300 86400 Access TTL in seconds (300 prod, 86400 debug)
user_type integer 100 9999 Role code; allowed values: 100, 200, 300, 400, 1000, 2000, 9999

Errors: 401 (login failures), 503 (database connection failed)

Example

curl -X POST http://127.0.0.1:8000/api/v3/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "admin@test.com", "password": "admin123"}'
{
  "access": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9...",
  "refresh": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9...",
  "exp_time": 300,
  "user_type": 100
}

2. Register

Name User registration
URL POST /api/v3/auth/register
Authentication None (public)
Authorization Any caller; always creates Customer (1000)
Allowed roles — (unauthenticated)
Operations Create

Request body (JSON)

Field Type Required Min length Max length Min value Max value Other
email string (email) Yes 1 320 Unique; valid email format
first_name string Yes 1 150
last_name string Yes 1 150
password string Yes 8
re_password string Yes 8 Must equal password
mobile string No 11 Unique if set; omit or null

New users are created as Customer (user_type=1000).

Response 201RegisterResponse

Field Type Min length Max length Min value Max value
id UUID 36 36
email string 1 320
first_name string 1 150
last_name string 1 150
message string 1

Errors: 400 (Email already registered, Mobile number already registered), 422 (Passwords do not match)


3. Refresh token

URL POST /api/v3/auth/token/refresh
Authentication None (public)
Authorization Any caller with a valid, non-expired refresh token
Allowed roles — (unauthenticated; role embedded in refresh token is not checked)
Operations Execute

Request body

Field Type Required Min length Max length Aliases Other
refresh string Yes refresh_token Valid JWT refresh token; empty/invalid → 401

Response 200

Field Type Min length Max length
access string 1
{ "access": "<new_jwt>" }

Errors: 401Invalid or expired refresh token


4. Verify token

URL POST /api/v3/auth/token/verify
Authentication None (public)
Authorization Any caller with a token to verify
Allowed roles
Operations Execute

Request body

Field Type Required Min length Max length Other
token string Yes JWT access or refresh; invalid → 401

Response 200: { "detail": "Token is valid" }

Errors: 401Invalid or expired token


5. Logout

URL POST /api/v3/auth/logout
Authentication Validated JWT access token
Authorization Any authenticated user
Allowed roles 100, 200, 300, 400, 1000, 2000, 9999
Operations Execute (self)

Request body

Field Type Required Min length Max length Other
refresh string Yes Valid JWT refresh token

Response 200: { "message": "Successfully logged out" }

Side effect: Marks user logout timestamp; invalidates refresh token server-side.


6. Change password

URL POST /api/v3/auth/password/change
Authentication Validated JWT access token
Authorization Self — changes password for JWT holder only
Allowed roles 100, 200, 300, 400, 1000, 2000, 9999
Operations Update (self)

Request body

Field Type Required Min length Max length Min value Max value Other
current_password string Yes Verified against stored hash
password string Yes 8 New password
re_password string Yes 8 Must equal password

Response 200: { "message": "Password changed successfully" }

Errors: 400 (User not found, Current password is incorrect), 422 (length / match validation)


7. Set password (manager)

URL POST /api/v3/auth/password/set
Authentication Validated JWT access token
Authorization Manager+ — set password for another user
Allowed roles System Manager (100), Manager (200)
Operations Manage

Request body

Field Type Required Min length Max length Min value Max value Other
user UUID Yes 36 36 Target user ID; must exist
password string Yes 8
re_password string Yes 8 Must equal password

Response 200: { "message": "Password set successfully" }

Errors: 403Only managers can set user passwords (Customer, Office Employee, Warehouse Employee, etc.); 422 — validation

Unauthorized example: Customer (1000) with valid token → 403


8. Password reset — send code

URL POST /api/v3/auth/password/reset
Authentication None (public)
Authorization Any caller; email must belong to registered, verified, non-banned user
Allowed roles
Operations Execute

Request body

Field Type Required Min length Max length Other
email string (email) Yes 1 320 Registered, verified, non-banned user

Response 200SendActivationResponse

Field Type Min Max Notes
timeout integer 900 900 Code validity in seconds (EMAIL_TOKEN_EXPIRE)
code integer 100000 999999 Only when debug=true

Errors: 400 (Email not registered, User is banned, Email not verified)


9. Password reset — confirm

URL POST /api/v3/auth/password/reset/confirm
Authentication None (public)
Authorization Any caller with valid reset code for the given email
Allowed roles
Operations Update

Request body

Field Type Required Min length Max length Min value Max value Other
email string (email) Yes 1 320
token integer Yes 100000 999999 6-digit code; expires after 900 s
password string Yes 8
re_password string Yes 8 Must equal password

Response 200: { "message": "Password reset successfully" }

Errors: 400 (Invalid token, Token has expired, …), 422 (validation)


10. Activation — send code

URL POST /api/v3/auth/activation/send
Authentication None (public)
Authorization Any caller; email must belong to existing, non-banned, unverified user
Allowed roles
Operations Execute

Request body

Field Type Required Min length Max length Min value Max value Other
email string (email) Yes 1 320 User must exist, not banned, not already verified

Response 200SendActivationResponse

Field Type Min Max
timeout integer 900 900
code integer 100000 999999 (debug only)

Errors: 400 (Email already verified, Email not registered, User is banned)


11. Activation — confirm

URL POST /api/v3/auth/activation/confirm
Authentication None (public)
Authorization Any caller with valid activation code for the given email
Allowed roles
Operations Update

Request body

Field Type Required Min length Max length Min value Max value Other
email string (email) Yes 1 320
token integer Yes 100000 999999 Expires after 900 s

Response 200: { "message": "Email activated successfully" }

Side effect: Sets email_verified=true, is_active=true, clears verification token.


12. Activate / deactivate user

URL POST /api/v3/auth/user/activate
Authentication Validated JWT access token
Authorization Manager+ — activate/deactivate another user
Allowed roles System Manager (100), Manager (200)
Operations Manage

Request body

Field Type Required Min length Max length Min value Max value Other
user UUID Yes 36 36 Target user ID
status boolean Yes true = activate, false = deactivate

Response 200: { "message": "User activated" } or "User deactivated"

Errors: 403Only managers can activate/deactivate users

Unauthorized example: Warehouse Employee (400) → 403


13. Ban / unban user

URL POST /api/v3/auth/user/ban
Authentication Validated JWT access token
Authorization Manager+ — ban/unban another user
Allowed roles System Manager (100), Manager (200)
Operations Manage

Request body

Field Type Required Min length Max length Min value Max value Other
user UUID Yes 36 36 Target user ID
status boolean Yes true = ban, false = unban

Response 200: { "message": "User banned" } or "User unbanned"


14. Create user (manager)

URL POST /api/v3/auth/user/create
Authentication Validated JWT access token
Authorization Staff+ with role-specific limits (see matrix)
Allowed roles System Manager (100), Manager (200), Office Employee (300)
Operations Create

Request body

Field Type Required Min length Max length Min value Max value Other
email string (email) Yes 1 320 Unique
first_name string Yes 1 150
last_name string Yes 1 150
user_type integer Yes 100 9999 Allowed: 100, 200, 300, 400, 1000, 2000, 9999; role restrictions apply
is_active boolean No Default true
mobile string No 11 Unique if set

Response 201RegisterResponse

Field Type Notes
id UUID
email string
first_name string Max 150
last_name string Max 150
message string Includes auto-generated 8-character password

Response 201: message includes generated password (send via email in production).

Errors: 403 (Insufficient permissions, role-specific restrictions), 400 (Email already registered)


Profile APIs (`/api/v3/users/profile`)

All routes require a validated JWT access token (JWTBearer applied at router level).

Authorization model: Self only — every profile endpoint reads or updates the user identified by the JWT user_id claim. No role may access another user's profile through these routes.

Role Read own profile Update own profile Upload avatar
System Manager (100) (501)
Manager (200) (501)
Office Employee (300) (501)
Warehouse Employee (400) (501)
Customer (1000) (501)
Customs Officer (2000) (501)
Undefined (9999) (501)

15. Get profile

URL GET /api/v3/users/profile/
Authentication Validated JWT access token
Authorization Self
Allowed roles Any authenticated (100–9999)
Operations Read (self)

Query parameters: None.

Response 200ProfileResponse

Field Type Min length Max length Min value Max value Notes
id UUID 36 36
full_name string 1 301 (computed)
first_name string 1 150
last_name string 1 150
email string 1 320
user_type integer 100 9999 Enum value
avatar string | null URL/path or null
two_step_auth boolean Default false
notify_after_login boolean Default false

16. Update profile

URL PATCH /api/v3/users/profile/
Authentication Validated JWT access token
Authorization Self
Allowed roles Any authenticated (100–9999)
Operations Update (self)

Query parameters: None.

Request body (all fields optional — send only fields to change)

Field Type Min length Max length Min value Max value Other
first_name string 1 150 Omit to leave unchanged
last_name string 1 150 Omit to leave unchanged
two_step_auth boolean
notify_after_login boolean

Response 200: ProfileResponse (same shape as GET profile)


17. Get detailed profile (`/me`)

URL GET /api/v3/users/profile/me
Authentication Validated JWT access token
Authorization Self
Allowed roles Any authenticated (100–9999)
Operations Read (self)

Query parameters: None.

Response 200UserDetailResponse

Field Type Min length Max length Min value Max value Notes
id UUID 36 36
full_name string 1 301 (computed)
first_name string 1 150
last_name string 1 150
email string 1 320
email_verified boolean
user_type integer 100 9999
is_active boolean
can_delete boolean Maps to deletable
mobile string | null 11
mobile_verified boolean
is_staff boolean
joined_at string | null 19 19 YYYY.MM.DD HH:MM:SS or null
logout_at string | null 19 19
login_at string | null 19 19
is_banned boolean
notify_after_login boolean
two_step_auth boolean
avatar string | null
created_at string | null 19 19
updated_at string | null 19 19

18. Upload avatar

URL POST /api/v3/users/profile/avatar
Authentication Validated JWT access token
Authorization Self
Allowed roles Any authenticated (100–9999)
Operations Update (self)
Status 501 Not Implemented{ "detail": "Avatar upload not yet implemented" }

User CRUD APIs (`/api/v3/user/v1/users`)

Low-level user table operations.

Authentication note: Protected routes use OAuth2PasswordBearer, which requires an Authorization: Bearer … header but does not validate the JWT signature, expiry, or role. Any non-empty Bearer token satisfies the dependency.

Authorization note: No role-based access control is implemented on these routes. Any caller with a Bearer header (or no header for public routes) can perform CRUD operations.

User CRUD access summary

# Endpoint Authentication Authorization Allowed roles
19 POST / Bearer header None Any (token not validated)
20 POST /bulk Bearer header None Any
21 GET /{user_id} Bearer header None Any
22 GET /email/{email} Bearer header None Any
23 GET /mobile/{mobile} Bearer header None Any
24 GET / Public None Any caller
25–26 PUT/PATCH /{user_id} Bearer header None Any
27 PUT /bulk Bearer header None Any
28 DELETE /{user_id} Bearer header None Any
29 DELETE /{user_id}/hard Bearer header None Any
30 POST /{user_id}/verify-email Bearer header None Any
31 POST /{user_id}/password Bearer header None Any
32–33 POST /{user_id}/log-login|log-logout Bearer header None Any
34 GET /test Public None Any caller

For production use, prefer the /api/v3/auth manager endpoints which enforce validated JWT and role checks.

19. Create user

POST /api/v3/user/v1/users/201 UserResponse

| Authentication | Bearer header (not validated) | | Authorization | None — no role check | | Allowed roles | Any caller presenting a Bearer token | | Operations | Create |

Request body

Field Type Required Min length Max length Min value Max value Other
first_name string Yes 150 (DB) No Pydantic min; empty may fail at DB
last_name string Yes 150 (DB)
password string Yes Stored as provided (not auto-hashed by CRUD)
email string No (DB: unlimited) Unique if set; no EmailStr validation
mobile string No 11 (DB) Unique if set
is_active boolean No Default true

Response 201UserResponse

Field Type Min length Max length Min value Max value
id UUID 36 36
email string | null
first_name string 150 (DB)
last_name string 150 (DB)
is_active boolean
mobile string | null 11
avatar string | null
created_at string | null 19 19
updated_at string | null 19 19

20. Bulk create

POST /api/v3/user/v1/users/bulk201 UserResponse[]

| Authentication | Bearer header (not validated) | | Authorization | None | | Operations | Create |

Request body

Field Type Required Min length Max length Min value Max value Other
users array of UserCreate Yes 1 (items) No max array size in schema; each item follows create-user rules

Query parameters: None. No pagination, sorting, or filtering.


21. Get user by ID

GET /api/v3/user/v1/users/{user_id}200 UserResponse | 404

| Authentication | Bearer header (not validated) | | Authorization | None — any role can read any user by ID | | Operations | Read |

Path parameters

Param Type Min length Max length Min value Max value Other
user_id UUID 36 36 Valid RFC 4122 UUID; invalid → 422

Query parameters: None. No sorting/filtering.


22. Get user by email

GET /api/v3/user/v1/users/email/{email}200 | 404

| Authentication | Bearer header (not validated) | | Authorization | None | | Operations | Read |

Path parameters

Param Type Min length Max length Other
email string 1 (DB: unlimited) Exact match; URL-encode @ as %40

Query parameters: None.


23. Get user by mobile

GET /api/v3/user/v1/users/mobile/{mobile}200 | 404

| Authentication | Bearer header (not validated) | | Authorization | None | | Operations | Read |

Path parameters

Param Type Min length Max length Other
mobile string 1 11 Exact match

Query parameters: None.


24. List users

GET /api/v3/user/v1/users/200 UserListResponse

| Authentication | None (public) | | Authorization | None — unauthenticated access allowed | | Allowed roles | Any caller | | Operations | Read (list) |

See Query, filtering, sorting & pagination for full details.

Query parameters

Param Type Required Default Min Max Description
offset integer No 0 0 Pagination offset
limit integer No 100 1 1000 Page size
active_only boolean No true Filter by is_active

Not supported: sort, order, search, q, user_type, email, or any other filter query params.

Auth note: Token check is commented out in source — fully public.

Unauthorized example: N/A — no authentication required.

Response 200UserListResponse

Field Type Description
users UserResponse[] Current page; each item follows UserResponse field limits
total integer Count of active users only; min 0
offset integer Echo of request; min 0
limit integer Echo of request; min 1, max 1000
{
  "users": [ { "id": "...", "first_name": "...", ... } ],
  "total": 42,
  "offset": 0,
  "limit": 100
}

25. Update user (full)

PUT /api/v3/user/v1/users/{user_id}200 UserResponse

| Authentication | Bearer header (not validated) | | Authorization | None — any role can update any user | | Operations | Update |

Path: user_id — UUID

Request body (UserUpdate — all fields optional)

Field Type Min length Max length Min value Max value Other
email string (DB: unlimited)
first_name string 150 (DB)
last_name string 150 (DB)
mobile string 11 (DB)
is_active boolean
avatar string

Protected fields (cannot update via CRUD): id, _created_at, _created_by


26. Partial update

PATCH /api/v3/user/v1/users/{user_id} — same body and constraints as PUT; only sent fields are updated.

| Authentication | Bearer header (not validated) | | Authorization | None | | Operations | Update |


27. Bulk update

PUT /api/v3/user/v1/users/bulk200 UserResponse[]

| Authentication | Bearer header (not validated) | | Authorization | None | | Operations | Update |

Request body

Field Type Required Min length Max length Other
users array Yes 1 (items) No max array size; implementation expects user_id per item

Query parameters: None.


28. Soft delete

DELETE /api/v3/user/v1/users/{user_id}204 (sets is_active=false)

| Authentication | Bearer header (not validated) | | Authorization | None | | Operations | Delete (soft) |

Path: user_id — UUID. No query parameters.


29. Hard delete

DELETE /api/v3/user/v1/users/{user_id}/hard204 (permanent row deletion)

| Authentication | Bearer header (not validated) | | Authorization | None | | Operations | Delete (hard) |

Path: user_id — UUID. No query parameters.


30. Verify email (admin)

POST /api/v3/user/v1/users/{user_id}/verify-email200 UserResponse

| Authentication | Bearer header (not validated) | | Authorization | None — no manager role required (unlike auth API) | | Operations | Manage |

Path: user_id — UUID. No request body or query parameters.


31. Update password (admin)

POST /api/v3/user/v1/users/{user_id}/password204

| Authentication | Bearer header (not validated) | | Authorization | None — no manager role required | | Operations | Manage |

Path: user_id — UUID

Query parameters

Param Type Required Min length Max length Min value Max value Other
new_password string Yes No schema validation; stored as provided (caller should pre-hash)

No request body.


32. Log login event

POST /api/v3/user/v1/users/{user_id}/log-login204

| Authentication | Bearer header (not validated) | | Authorization | None | | Operations | Execute |

Path: user_id — UUID. No body or query params. Sets login_at and is_logout=false.


33. Log logout event

POST /api/v3/user/v1/users/{user_id}/log-logout204

| Authentication | Bearer header (not validated) | | Authorization | None | | Operations | Execute |

Path: user_id — UUID. No body or query params. Sets logout_at and is_logout=true.


34. Database health check

GET /api/v3/user/v1/users/test200 { "db_ok": true }

| Authentication | None (public) | | Authorization | None | | Operations | Execute (health) |


Action Database / state changes
Login _login_at updated, is_logout=false
Logout Refresh invalidated, _logout_at updated
Register New core_user row, user_type=1000, password hashed (PBKDF2)
Email activation email_verified=true, token cleared
Password change password hash updated
Soft delete is_active=false
Ban (auth API) is_banned toggled

Email / SMS: Activation and reset codes are generated server-side; email delivery is TODO in several paths. In debug mode, codes are returned in the API response.


Quick reference — all endpoints

Legend: [Public] = no auth · [JWT] = validated access token · [Bearer] = header only, not validated · [Mgr+] = Manager or System Manager · [Staff+] = System Manager, Manager, or Office Employee · [Self] = own account only

POST   /api/v3/auth/login                                    [Public]
POST   /api/v3/auth/register                                 [Public]
POST   /api/v3/auth/token/refresh                            [Public]
POST   /api/v3/auth/token/verify                             [Public]
POST   /api/v3/auth/logout                                   [JWT] Any role
POST   /api/v3/auth/password/change                          [JWT] [Self]
POST   /api/v3/auth/password/set                             [JWT] [Mgr+]
POST   /api/v3/auth/password/reset                           [Public]
POST   /api/v3/auth/password/reset/confirm                   [Public]
POST   /api/v3/auth/activation/send                          [Public]
POST   /api/v3/auth/activation/confirm                       [Public]
POST   /api/v3/auth/user/activate                            [JWT] [Mgr+]
POST   /api/v3/auth/user/ban                                 [JWT] [Mgr+]
POST   /api/v3/auth/user/create                              [JWT] [Staff+] role limits apply

GET    /api/v3/users/profile/                                [JWT] [Self]
PATCH  /api/v3/users/profile/                                [JWT] [Self]
GET    /api/v3/users/profile/me                              [JWT] [Self]
POST   /api/v3/users/profile/avatar                          [JWT] [Self] 501

POST   /api/v3/user/v1/users/                                [Bearer] no role check
POST   /api/v3/user/v1/users/bulk                            [Bearer] no role check
GET    /api/v3/user/v1/users/                                 [Public]
GET    /api/v3/user/v1/users/{user_id}                       [Bearer] no role check
GET    /api/v3/user/v1/users/email/{email}                   [Bearer] no role check
GET    /api/v3/user/v1/users/mobile/{mobile}                 [Bearer] no role check
PUT    /api/v3/user/v1/users/{user_id}                        [Bearer] no role check
PATCH  /api/v3/user/v1/users/{user_id}                      [Bearer] no role check
PUT    /api/v3/user/v1/users/bulk                            [Bearer] no role check
DELETE /api/v3/user/v1/users/{user_id}                        [Bearer] no role check
DELETE /api/v3/user/v1/users/{user_id}/hard                  [Bearer] no role check
POST   /api/v3/user/v1/users/{user_id}/verify-email          [Bearer] no role check
POST   /api/v3/user/v1/users/{user_id}/password              [Bearer] no role check
POST   /api/v3/user/v1/users/{user_id}/log-login             [Bearer] no role check
POST   /api/v3/user/v1/users/{user_id}/log-logout            [Bearer] no role check
GET    /api/v3/user/v1/users/test                            [Public]
On this page