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:
| Permission | Purpose |
|---|---|
admin:user:read | View user info, statistics, password expiration stats |
admin:user:create | Create users |
admin:user:update | Update users, activate/deactivate, password lifecycle |
admin:user:delete | Delete users |
Current User Endpoints
Get Current User
GET /api/v1/users/meReturns 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/meUpdate 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"
}'| Field | Type | Required | Description |
|---|---|---|---|
username | string | No | New username (must be unique) |
email | string | No | New email (must be unique; requires verification code if email verification is enabled) |
email_verification_code | string | No | Verification code required when changing email |
avatar_url | string | No | Avatar image URL |
locale | string | No | Interface 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-passwordChange 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"
}'| Field | Type | Required | Description |
|---|---|---|---|
current_password | string | Yes | Current password |
new_password | string | Yes | New 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/usersGet 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 Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
page | integer | No | 1 | Page number |
page_size | integer | No | 20 | Items per page |
status | array | No | - | Filter by status: active, inactive, pending (repeatable) |
search | string | No | - | Search by username or email |
role | array | No | - | Filter by role name (repeatable) |
exclude_user_id | array | No | - | 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 Parameter | Type | Required | Description |
|---|---|---|---|
user_id | string | Yes | User UUID |
Success (200 OK): Returns a single UserSchema with the same structure as list items.
Create User
POST /api/v1/admin/usersCreate 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"
}'| Field | Type | Required | Description |
|---|---|---|---|
username | string | Yes | Username (unique) |
email | string | Yes | Email (unique) |
password | string | Yes | Initial password |
is_active | boolean | No | Active status (default: true) |
approval_status | string | No | Approval status (default: approved) |
is_superuser | boolean | No | Superuser flag (default: false) |
avatar_url | string | No | Avatar image URL |
locale | string | No | Interface 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 Parameter | Type | Required | Description |
|---|---|---|---|
user_id | string | Yes | User UUID |
| Field | Type | Required | Description |
|---|---|---|---|
email | string | No | New email |
password | string | No | New password |
is_active | boolean | No | Active status |
avatar_url | string | No | Avatar image URL |
locale | string | No | Interface language |
roles | array | No | Role 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}/deactivateToggle 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-changeSets 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-expirationResets 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-statsRequires 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=expiringRequires 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/statsRequires admin:user:read. Response is 200 OK with counts total, active, inactive, and pending.
Error Codes
| Code | Message | Description |
|---|---|---|
4001 | User not found | User does not exist |
2003 | Invalid credentials | Wrong password |
3000 | Permission denied | Insufficient permissions |
1001 | Validation failed | Invalid request data |
5002 | Username already exists | Username is taken |
5003 | Email already exists | Email 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);Related Documentation
- Authentication - Authentication methods
- Admin API Boundaries - Admin console endpoint permission boundaries
How is this guide?