ClouisleClouisle

Users API

Manage current user profile and password, plus admin user CRUD and password lifecycle endpoints

Overview

The Users API provides current user profile/password management, plus admin user CRUD and password lifecycle operations.

  • Current user: GET/PUT /api/v1/users/me, POST /api/v1/users/me/change-password
  • Admin: list, detail, create, update, delete, activate/deactivate, and password lifecycle under /api/v1/admin/users

Admin endpoints require the corresponding admin:user:* permission; current user endpoints require an authenticated JWT session.

Authentication

All endpoints require Authorization: Bearer <token>.

Admin endpoints additionally require:

PermissionPurpose
admin:user:readView user info, statistics, password expiration stats
admin:user:createCreate users
admin:user:updateUpdate users, activate/deactivate, password lifecycle
admin:user:deleteDelete users

Current User Endpoints

Get Current User

GET /api/v1/users/me

Returns the full profile of the currently authenticated user.

curl -X GET "https://your-domain.com/api/v1/users/me" \
  -H "Authorization: Bearer YOUR_TOKEN"

Success (200 OK):

{
  "code": 0,
  "data": {
    "id": "user-123",
    "username": "johndoe",
    "email": "john.doe@example.com",
    "is_active": true,
    "approval_status": "approved",
    "is_superuser": false,
    "avatar_url": "https://example.com/avatars/johndoe.jpg",
    "locale": "en",
    "created_at": "2026-01-15T10:00:00Z",
    "last_login": "2026-02-11T14:30:00Z",
    "auth_source": "local",
    "external_id": null,
    "email_verified": true,
    "force_password_change": false,
    "password_expiration_exempt": false,
    "status": "active",
    "roles": [],
    "sso_connections": []
  },
  "msg": "success"
}

Update Current User

PUT /api/v1/users/me

Update the currently authenticated user's profile. All fields are optional; include only the fields you want to update.

curl -X PUT "https://your-domain.com/api/v1/users/me" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "johnsmith",
    "avatar_url": "https://example.com/avatars/new-avatar.jpg"
  }'
FieldTypeRequiredDescription
usernamestringNoNew username (must be unique)
emailstringNoNew email (must be unique; requires verification code if email verification is enabled)
email_verification_codestringNoVerification code required when changing email
avatar_urlstringNoAvatar image URL
localestringNoInterface language (e.g. en, zh)

Success (200 OK):

{
  "code": 0,
  "data": {
    "id": "user-123",
    "username": "johnsmith",
    "email": "john.doe@example.com",
    "is_active": true,
    "approval_status": "approved",
    "is_superuser": false,
    "avatar_url": "https://example.com/avatars/new-avatar.jpg",
    "locale": "en",
    "created_at": "2026-01-15T10:00:00Z",
    "last_login": "2026-02-11T14:30:00Z",
    "auth_source": "local",
    "external_id": null,
    "email_verified": true,
    "force_password_change": false,
    "password_expiration_exempt": false,
    "status": "active",
    "roles": [],
    "sso_connections": []
  },
  "msg": "Profile updated successfully"
}

Change Password

POST /api/v1/users/me/change-password

Change the currently authenticated user's password.

curl -X POST "https://your-domain.com/api/v1/users/me/change-password" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "current_password": "old_password",
    "new_password": "new_secure_password"
  }'
FieldTypeRequiredDescription
current_passwordstringYesCurrent password
new_passwordstringYesNew password (validated against password policy)

Success (200 OK):

{
  "code": 0,
  "data": null,
  "msg": "Password changed successfully"
}

Error (400 Bad Request): When the current password is incorrect, BusinessError uses the default 400 transport status; the response code remains 2003 (INVALID_CREDENTIALS).

{
  "code": 2003,
  "data": null,
  "msg": "Current password is incorrect"
}

Admin Endpoints

List Users

GET /api/v1/admin/users

Get a list of all users (admin only), with pagination, status filtering, search, and role filtering.

curl -X GET "https://your-domain.com/api/v1/admin/users?page=1&page_size=20" \
  -H "Authorization: Bearer YOUR_ADMIN_TOKEN"
Query ParameterTypeRequiredDefaultDescription
pageintegerNo1Page number
page_sizeintegerNo20Items per page
statusarrayNo-Filter by status: active, inactive, pending (repeatable)
searchstringNo-Search by username or email
rolearrayNo-Filter by role name (repeatable)
exclude_user_idarrayNo-User IDs to exclude from results (repeatable)

Success (200 OK):

{
  "code": 0,
  "data": {
    "items": [
      {
        "id": "user-123",
        "username": "johndoe",
        "email": "john.doe@example.com",
        "is_active": true,
        "approval_status": "approved",
        "is_superuser": false,
        "avatar_url": "https://example.com/avatars/johndoe.jpg",
        "locale": "en",
        "created_at": "2026-01-15T10:00:00Z",
        "last_login": "2026-02-11T14:30:00Z",
        "auth_source": "local",
        "external_id": null,
        "email_verified": true,
        "force_password_change": false,
        "password_expiration_exempt": false,
        "status": "active",
        "roles": [],
        "sso_connections": []
      }
    ],
    "total": 156,
    "page": 1,
    "page_size": 20
  },
  "msg": "success"
}

Get User

GET /api/v1/admin/users/{user_id}

Get details of a specific user (admin only).

curl -X GET "https://your-domain.com/api/v1/admin/users/user-123" \
  -H "Authorization: Bearer YOUR_ADMIN_TOKEN"
Path ParameterTypeRequiredDescription
user_idstringYesUser UUID

Success (200 OK): Returns a single UserSchema with the same structure as list items.

Create User

POST /api/v1/admin/users

Create a new user (admin only).

curl -X POST "https://your-domain.com/api/v1/admin/users" \
  -H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "alice",
    "email": "alice@example.com",
    "password": "secure_password"
  }'
FieldTypeRequiredDescription
usernamestringYesUsername (unique)
emailstringYesEmail (unique)
passwordstringYesInitial password
is_activebooleanNoActive status (default: true)
approval_statusstringNoApproval status (default: approved)
is_superuserbooleanNoSuperuser flag (default: false)
avatar_urlstringNoAvatar image URL
localestringNoInterface language (default: en)

Success (200 OK):

{
  "code": 0,
  "data": {
    "id": "user-789",
    "username": "alice",
    "email": "alice@example.com",
    "is_active": true,
    "approval_status": "approved",
    "is_superuser": false,
    "avatar_url": null,
    "locale": "en",
    "created_at": "2026-02-11T16:00:00Z",
    "last_login": null,
    "auth_source": "local",
    "external_id": null,
    "email_verified": false,
    "force_password_change": false,
    "password_expiration_exempt": false,
    "status": "active",
    "roles": [],
    "sso_connections": []
  },
  "msg": "User created successfully"
}

Error (409 Conflict):

{ "code": 5002, "data": null, "msg": "Username already exists" }
{ "code": 5003, "data": null, "msg": "Email already exists" }

Update User

PUT /api/v1/admin/users/{user_id}

Update a user's information (admin only). All fields are optional; include only the fields you want to update.

curl -X PUT "https://your-domain.com/api/v1/admin/users/user-789" \
  -H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "is_active": true,
    "roles": ["admin"]
  }'
Path ParameterTypeRequiredDescription
user_idstringYesUser UUID
FieldTypeRequiredDescription
emailstringNoNew email
passwordstringNoNew password
is_activebooleanNoActive status
avatar_urlstringNoAvatar image URL
localestringNoInterface language
rolesarrayNoRole names to assign

Success (200 OK):

{
  "code": 0,
  "data": {
    "id": "user-789",
    "username": "alice",
    "email": "alice@example.com",
    "is_active": true,
    "approval_status": "approved",
    "is_superuser": false,
    "avatar_url": null,
    "locale": "en",
    "created_at": "2026-02-11T16:00:00Z",
    "last_login": null,
    "auth_source": "local",
    "external_id": null,
    "email_verified": false,
    "force_password_change": false,
    "password_expiration_exempt": false,
    "status": "active",
    "roles": [
      {
        "id": "role-admin",
        "name": "admin",
        "description": null,
        "is_system_role": true,
        "permissions": []
      }
    ],
    "sso_connections": []
  },
  "msg": "User updated successfully"
}

Delete User

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

Permanently delete a user (admin only).

curl -X DELETE "https://your-domain.com/api/v1/admin/users/user-789" \
  -H "Authorization: Bearer YOUR_ADMIN_TOKEN"

Success (200 OK):

{
  "code": 0,
  "data": null,
  "msg": "User deleted successfully"
}

Activate / Deactivate User

POST /api/v1/admin/users/{user_id}/activate
POST /api/v1/admin/users/{user_id}/deactivate

Toggle a user's active status.

curl -X POST "https://your-domain.com/api/v1/admin/users/user-789/deactivate" \
  -H "Authorization: Bearer YOUR_ADMIN_TOKEN"

Success (200 OK): Returns the updated UserSchema with is_active toggled.

Password Lifecycle Management (Admin)

There is no direct admin password-reset endpoint. Administrators manage password-expiration state with these JWT-authenticated endpoints; each requires admin:user:update unless noted.

Force Password Change

POST /api/v1/admin/users/{user_id}/force-password-change

Sets force_password_change so the user must change the password at next login. Response is 200 OK with data: null.

Reset Password Expiration

POST /api/v1/admin/users/{user_id}/reset-password-expiration

Resets the password-change timestamp and recalculates expiration. Response is 200 OK with data: null.

Set Expiration Exemption

POST /api/v1/admin/users/{user_id}/exempt-password-expiration
{"exempt": true}

Sets whether the user is exempt from password expiration. Response is 200 OK with data: null.

Force Password Change for Multiple Users

POST /api/v1/admin/users/bulk-force-password-change
{"user_ids": ["user-123", "user-456"]}

Response is 200 OK with data: {"count": 2} (the count reflects users actually updated).

Password Expiration Statistics

GET /api/v1/admin/users/password-expiration-stats

Requires admin:user:read. Response contains total_users, expired_count, expiring_soon_count, force_change_count, and exempt_count.

List Expiring Passwords

GET /api/v1/admin/users/expiring-passwords?page=1&page_size=20&filter=expiring

Requires admin:user:read. filter is one of all, expired, expiring, or force_change; page_size is bounded to 1–100. The paginated response uses only items, total, page, and page_size.

User Statistics

GET /api/v1/admin/users/stats

Requires admin:user:read. Response is 200 OK with counts total, active, inactive, and pending.

Error Codes

CodeMessageDescription
4001User not foundUser does not exist
2003Invalid credentialsWrong password
3000Permission deniedInsufficient permissions
1001Validation failedInvalid request data
5002Username already existsUsername is taken
5003Email already existsEmail is taken

No per-endpoint rate limits are implemented. There is no rate-limit middleware on these endpoints.

Best Practices

Profile Updates

  • Validate email format
  • Use strong passwords
  • Update preferences regularly
  • Keep profile information current

Don't share account credentials, use weak passwords, skip email verification, or ignore security settings.

Admin Operations

  • Verify user information before creation
  • Use strong initial passwords
  • Send welcome emails
  • Document user changes
  • Review user activity regularly

Don't create users without verification, use default passwords, skip welcome emails, forget to audit changes, or delete users without backup.

Code Examples

Python

import requests

def get_current_user(token):
    """Get current user information."""
    url = "https://your-domain.com/api/v1/users/me"
    headers = {"Authorization": f"Bearer {token}"}
    response = requests.get(url, headers=headers)
    result = response.json()
    if result['code'] == 0:
        return result['data']
    raise Exception(f"Error: {result['msg']}")

def update_profile(token, username, avatar_url=None, locale=None):
    """Update user profile."""
    url = "https://your-domain.com/api/v1/users/me"
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }
    data = {"username": username, "avatar_url": avatar_url, "locale": locale}
    response = requests.put(url, headers=headers, json=data)
    result = response.json()
    if result['code'] == 0:
        return result['data']
    raise Exception(f"Error: {result['msg']}")

# Usage
user = get_current_user("YOUR_TOKEN")
print(f"User: {user['username']}")
updated = update_profile("YOUR_TOKEN", "johnsmith", locale="en")
print(f"Updated: {updated['username']}")

JavaScript

async function getCurrentUser(token) {
  const response = await fetch(
    'https://your-domain.com/api/v1/users/me',
    { headers: { 'Authorization': `Bearer ${token}` } }
  );
  const result = await response.json();
  if (result.code === 0) return result.data;
  throw new Error(result.msg);
}

async function updateProfile(token, username, avatarUrl = null, locale = null) {
  const response = await fetch(
    'https://your-domain.com/api/v1/users/me',
    {
      method: 'PUT',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ username, avatar_url: avatarUrl, locale }),
    }
  );
  const result = await response.json();
  if (result.code === 0) return result.data;
  throw new Error(result.msg);
}

// Usage
const user = await getCurrentUser('YOUR_TOKEN');
console.log('User:', user.username);
const updated = await updateProfile('YOUR_TOKEN', 'johnsmith', null, 'en');
console.log('Updated:', updated.username);

How is this guide?

On this page