User Management¶
Complete guide to managing user accounts, roles, permissions, and outlet assignments in the Reserva platform.
Overview¶
The user management system provides comprehensive account administration with support for:
- User CRUD Operations - Create, read, update, and delete user accounts
- Role-Based Access Control - Hierarchical permission system (SUPER_ADMIN → TENANT_ADMIN → OUTLET_MANAGER → STAFF)
- Outlet Assignments - Manage which outlets users can access
- Password Management - Administrative password reset with temporary credentials
- User Statistics - Analytics and reporting on user accounts
- Subscription Limits - Enforce user count restrictions based on subscription plan
Key Concepts:
- Role Hierarchy = Controls who can manage whom (cannot manage equal or higher roles)
- Tenant Isolation = Users can only see/manage accounts within their tenant
- Outlet Scoping = Outlet managers restricted to their assigned outlets
- Soft Deletion = User accounts deactivated, not permanently removed
- Subscription Enforcement = User creation respects plan limits
Subscription Limits¶
User creation is subject to subscription plan limits:
| Plan | Max Outlets | Max Staff per Outlet | Total Users Impact |
|---|---|---|---|
| FREE | 1 outlet | 5 staff per outlet | Max 5 staff + admins |
| PRO | 10 outlets | 50 staff per outlet | Max 500 staff + admins |
| ENTERPRISE | Unlimited | Unlimited | Unlimited users |
Enforcement Rules:
- Staff user creation checks
max_staff_per_outletlimit for assigned outlets - Creating outlets checks
max_outletslimit from subscription - Exceeding limits returns HTTP 403 with upgrade recommendation
- Admins (TENANT_ADMIN, OUTLET_MANAGER) do not count toward staff limits
- Deactivated users still count toward limits until fully deleted
Role Hierarchy¶
The platform uses a strict role hierarchy for access control:
SUPER_ADMIN (Platform administrators)
↓
TENANT_ADMIN (Business owners)
↓
OUTLET_MANAGER (Location managers)
↓
STAFF (Service providers)
Permissions by Role:
| Action | SUPER_ADMIN | TENANT_ADMIN | OUTLET_MANAGER | STAFF |
|---|---|---|---|---|
| View all users | ✅ All tenants | ✅ Own tenant | ✅ Own outlets | ❌ |
| Create users | ✅ Any role | ✅ Except SUPER_ADMIN | ✅ STAFF only | ❌ |
| Update users | ✅ Anyone | ✅ Own tenant | ✅ Own outlets | ✅ Self only |
| Delete users | ✅ Except SUPER_ADMIN | ✅ Lower roles | ✅ STAFF only | ❌ |
| Reset passwords | ✅ Anyone | ✅ Own tenant | ✅ Own outlets | ❌ |
| Manage outlets | ✅ Anyone | ✅ Own tenant | ✅ Own outlets | ❌ |
| View statistics | ✅ All | ✅ Own tenant | ✅ Own outlets | ❌ |
List Users¶
Get paginated list of users with advanced filtering and role-based access control.
Endpoint¶
Authentication: Required (JWT token)
Query Parameters¶
page(optional) - Page number, default: 1size(optional) - Items per page, default: 20, max: 100role(optional) - Filter by role:SUPER_ADMIN,TENANT_ADMIN,OUTLET_MANAGER,STAFFoutlet_id(optional) - Filter by outlet assignmentsearch(optional) - Search by name or email (case-insensitive)is_active(optional) - Filter by active status:trueorfalseinclude_locked(optional) - Include locked accounts, default:false
Response¶
{
"items": [
{
"id": "60f1b2b3b3b3b3b3b3b3b3b3",
"email": "john.doe@example.com",
"first_name": "John",
"last_name": "Doe",
"phone": "+6281234567890",
"role": "STAFF",
"tenant_ids": ["507f1f77bcf86cd799439011"],
"outlet_ids": ["507f1f77bcf86cd799439012"],
"is_active": true,
"is_locked": false,
"avatar_url": "https://example.com/avatar.jpg",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-20T14:45:00Z"
}
],
"total": 45,
"page": 1,
"size": 20,
"pages": 3
}
Access Control¶
- SUPER_ADMIN: View all users across all tenants (or filtered by tenant context)
- TENANT_ADMIN: View all users within their tenant
- OUTLET_MANAGER: View users within their managed outlets only
- STAFF: Cannot access this endpoint (use
/users/meinstead)
Examples¶
Search by email:
curl -X GET "http://localhost:8000/api/v1/users?search=john" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Filter by role and outlet:
curl -X GET "http://localhost:8000/api/v1/users?role=STAFF&outlet_id=507f1f77bcf86cd799439012" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Include locked accounts:
curl -X GET "http://localhost:8000/api/v1/users?include_locked=true" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Create User¶
Create a new user account with role assignment and outlet allocation.
Endpoint¶
Authentication: Required (JWT token)
Request Body¶
{
"email": "jane.smith@example.com",
"password": "SecurePass123!",
"first_name": "Jane",
"last_name": "Smith",
"phone": "+6281234567891",
"role": "STAFF",
"tenant_ids": ["507f1f77bcf86cd799439011"],
"outlet_ids": ["507f1f77bcf86cd799439012"],
"send_welcome_email": true
}
Parameters:
email(required) - Unique email addresspassword(optional) - User password (auto-generated if not provided)first_name(required) - User's first namelast_name(required) - User's last namephone(optional) - Phone number with country coderole(required) - User role (subject to hierarchy restrictions)tenant_ids(required) - Array of tenant IDs (auto-assigned for non-super-admins)outlet_ids(optional) - Array of outlet IDs (validated against tenant)send_welcome_email(optional) - Send welcome email with credentials, default:false
Response¶
{
"id": "60f1b2b3b3b3b3b3b3b3b3b4",
"email": "jane.smith@example.com",
"first_name": "Jane",
"last_name": "Smith",
"phone": "+6281234567891",
"role": "STAFF",
"tenant_ids": ["507f1f77bcf86cd799439011"],
"outlet_ids": ["507f1f77bcf86cd799439012"],
"is_active": true,
"is_locked": false,
"must_change_password": false,
"created_at": "2025-01-21T09:15:00Z",
"updated_at": "2025-01-21T09:15:00Z"
}
Access Control¶
- SUPER_ADMIN: Can create users with any role in any tenant
- TENANT_ADMIN: Can create users within their tenant (except SUPER_ADMIN)
- OUTLET_MANAGER: Can create STAFF users for their managed outlets only
- STAFF: Cannot create users
Business Rules¶
- Email Uniqueness: Email must be unique across entire system
- Role Hierarchy: Cannot create users with role equal to or higher than your own
- Tenant Validation: All assigned tenants must exist
- Outlet Validation: All assigned outlets must:
- Exist in the system
- Belong to one of the user's assigned tenants
- Subscription Limits: Staff creation checks subscription limits:
- FREE plan: Max 5 staff per outlet
- PRO plan: Max 50 staff per outlet
- ENTERPRISE: Unlimited
- Password Policy:
- Auto-generated passwords are marked for mandatory change on first login
- Custom passwords must meet minimum security requirements
- Outlet Assignment:
- Outlet managers can only assign users to outlets they manage
- Tenant admins inherit tenant context automatically
Subscription Limit Enforcement¶
When creating a STAFF user, the system checks:
# Get current staff count for each outlet
for outlet_id in user_in.outlet_ids:
current_staff_count = await count_staff_in_outlet(outlet_id)
subscription = await get_tenant_subscription(tenant_id)
if subscription.plan_type == "FREE" and current_staff_count >= 5:
raise HTTPException(
status_code=403,
detail="Staff limit reached for FREE plan. Upgrade to PRO for up to 50 staff per outlet."
)
elif subscription.plan_type == "PRO" and current_staff_count >= 50:
raise HTTPException(
status_code=403,
detail="Staff limit reached for PRO plan. Upgrade to ENTERPRISE for unlimited staff."
)
Error Responses¶
409 Conflict - Email already exists:
403 Forbidden - Subscription limit reached:
{
"detail": "Staff limit reached for FREE plan (5/5). Upgrade to PRO for up to 50 staff per outlet.",
"error_code": "SUBSCRIPTION_LIMIT_EXCEEDED",
"upgrade_url": "/api/v1/subscriptions/upgrade"
}
403 Forbidden - Role hierarchy violation:
404 Not Found - Invalid outlet:
Get Current User Profile¶
Retrieve the authenticated user's own profile information.
Endpoint¶
Authentication: Required (JWT token)
Response¶
{
"id": "60f1b2b3b3b3b3b3b3b3b3b3",
"email": "john.doe@example.com",
"first_name": "John",
"last_name": "Doe",
"phone": "+6281234567890",
"role": "STAFF",
"tenant_ids": ["507f1f77bcf86cd799439011"],
"outlet_ids": ["507f1f77bcf86cd799439012"],
"is_active": true,
"is_locked": false,
"must_change_password": false,
"avatar_url": "https://example.com/avatar.jpg",
"last_login_at": "2025-01-21T08:30:00Z",
"password_changed_at": "2025-01-15T10:00:00Z",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-20T14:45:00Z"
}
Access Control¶
- All authenticated users can access their own profile
- No additional permissions required beyond valid JWT token
Use Cases¶
- Display user information in UI header/profile section
- Check current role and permissions
- Verify outlet assignments
- Show account status and security settings
Get User by ID¶
Retrieve a specific user's profile by their unique ID.
Endpoint¶
Authentication: Required (JWT token)
Path Parameters¶
user_id(required) - User's unique ObjectId
Response¶
Same structure as /users/me endpoint.
Access Control¶
- SUPER_ADMIN: Can view any user profile
- TENANT_ADMIN: Can view users within their tenant
- OUTLET_MANAGER: Can view users in their managed outlets (must share at least one outlet)
- STAFF: Can only view their own profile (redirected to use
/users/me)
Examples¶
curl -X GET "http://localhost:8000/api/v1/users/60f1b2b3b3b3b3b3b3b3b3b3" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Error Responses¶
404 Not Found - User doesn't exist:
403 Forbidden - Insufficient permissions:
Update User¶
Update a user's information with role-based field restrictions.
Endpoint¶
Authentication: Required (JWT token)
Path Parameters¶
user_id(required) - User's unique ObjectId
Request Body¶
{
"first_name": "Jane",
"last_name": "Smith",
"phone": "+6281234567892",
"role": "OUTLET_MANAGER",
"outlet_ids": ["507f1f77bcf86cd799439012", "507f1f77bcf86cd799439013"],
"is_active": true,
"avatar_url": "https://example.com/new-avatar.jpg"
}
Updatable Fields (role-dependent):
| Field | SUPER_ADMIN | TENANT_ADMIN | OUTLET_MANAGER | STAFF (self) |
|---|---|---|---|---|
| first_name | ✅ | ✅ | ✅ | ✅ |
| last_name | ✅ | ✅ | ✅ | ✅ |
| phone | ✅ | ✅ | ✅ | ✅ |
| avatar_url | ✅ | ✅ | ✅ | ✅ |
| ✅ | ✅ | ❌ | ❌ | |
| role | ✅ | ✅ (restrictions) | ❌ | ❌ |
| tenant_ids | ✅ | ❌ | ❌ | ❌ |
| outlet_ids | ✅ | ✅ | ✅ (own outlets) | ❌ |
| is_active | ✅ | ✅ | ❌ | ❌ |
| is_locked | ✅ | ✅ | ❌ | ❌ |
Response¶
Returns updated user object with same structure as GET response.
Access Control¶
- SUPER_ADMIN: Can update any user with any changes
- TENANT_ADMIN: Can update users within tenant (cannot promote to SUPER_ADMIN)
- OUTLET_MANAGER: Can update staff in managed outlets (no role changes)
- STAFF: Can only update own profile (limited fields: first_name, last_name, phone, avatar_url)
Business Rules¶
-
Role Modification Protection:
-
Cannot modify SUPER_ADMIN accounts unless you are SUPER_ADMIN
- Cannot promote users to roles higher than your own
-
OUTLET_MANAGER cannot change any user's role
-
Tenant Boundary Enforcement:
-
Cannot update users from other tenants
-
Tenant assignment changes require SUPER_ADMIN
-
Outlet Assignment Validation:
-
All outlets must exist and belong to user's tenant
- OUTLET_MANAGER can only assign to outlets they manage
-
Changes validated against subscription limits
-
Field Restrictions:
-
STAFF can only update:
first_name,last_name,phone,avatar_url - Attempting to update restricted fields returns 403 error
Examples¶
Staff updating own profile:
curl -X PUT "http://localhost:8000/api/v1/users/60f1b2b3b3b3b3b3b3b3b3b3" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"first_name": "Jonathan",
"phone": "+6281234567899"
}'
Admin promoting user to manager:
curl -X PUT "http://localhost:8000/api/v1/users/60f1b2b3b3b3b3b3b3b3b3b3" \
-H "Authorization: Bearer ADMIN_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"role": "OUTLET_MANAGER",
"outlet_ids": ["507f1f77bcf86cd799439012"]
}'
Error Responses¶
403 Forbidden - Field restriction violation:
403 Forbidden - Role hierarchy violation:
Delete User¶
Soft delete a user account by marking as inactive and deleted.
Endpoint¶
Authentication: Required (JWT token)
Path Parameters¶
user_id(required) - User's unique ObjectId
Response¶
Access Control¶
- SUPER_ADMIN: Can delete any user except other SUPER_ADMINs
- TENANT_ADMIN: Can delete users within tenant (no admins, no self)
- OUTLET_MANAGER: Can delete STAFF in managed outlets only
- STAFF: Cannot delete any users
Business Rules¶
- Self-Deletion Prevention: Cannot delete your own account
- Role Protection: Cannot delete users with same or higher role
- Already Deleted Check: Returns 400 if user already deactivated
- Soft Delete: User marked as
is_active: false,is_deleted: true, data preserved - Tenant Isolation: Can only delete users within accessible scope
Deletion Behavior¶
When a user is deleted:
- is_active set to false
- is_deleted set to true
- deleted_at timestamp recorded
- User data preserved for audit purposes
- User cannot log in anymore
- User still counts toward subscription limits until purged
- Scheduled appointments remain active (business continuity)
Examples¶
curl -X DELETE "http://localhost:8000/api/v1/users/60f1b2b3b3b3b3b3b3b3b3b3" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Error Responses¶
400 Bad Request - Self-deletion attempt:
400 Bad Request - Already deleted:
403 Forbidden - Insufficient permissions:
Reset User Password¶
Administratively reset a user's password with optional auto-generation.
Endpoint¶
Authentication: Required (JWT token)
Path Parameters¶
user_id(required) - User's unique ObjectId
Request Body¶
Parameters:
new_password(optional) - Custom password (auto-generated if not provided)force_change(optional) - Require password change on next login, default:truesend_notification(optional) - Send email notification to user, default:false
Response¶
With auto-generated password:
With custom password:
Access Control¶
- SUPER_ADMIN: Can reset any user's password
- TENANT_ADMIN: Can reset passwords within tenant (except SUPER_ADMIN)
- OUTLET_MANAGER: Can reset STAFF passwords in managed outlets
- STAFF: Cannot use this endpoint (use profile endpoint for own password)
Password Reset Behavior¶
When password is reset:
hashed_passwordupdated with new secure hashmust_change_passwordset based onforce_changeparameterpassword_changed_attimestamp updatedfailed_login_attemptsreset to 0is_lockedset tofalse(account unlocked)locked_untilcleared- Optional email notification sent with temporary credentials
Security Rules¶
- Self-Reset Prevention: Cannot reset your own password via admin endpoint
- Auto-Generated Passwords: Cryptographically secure, 12+ characters
- Forced Password Change: Auto-generated passwords require change on first login
- Account Unlock: Password reset automatically unlocks locked accounts
- Audit Trail: Reset action logged with admin user ID and timestamp
Examples¶
Auto-generate password:
curl -X POST "http://localhost:8000/api/v1/users/60f1b2b3b3b3b3b3b3b3b3b3/reset-password" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"force_change": true,
"send_notification": true
}'
Set custom password:
curl -X POST "http://localhost:8000/api/v1/users/60f1b2b3b3b3b3b3b3b3b3b3/reset-password" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"new_password": "SecurePass123!",
"force_change": false
}'
Error Responses¶
400 Bad Request - Self-reset attempt:
403 Forbidden - Cannot reset super admin:
Update Outlet Assignments¶
Update which outlets a user is assigned to.
Endpoint¶
Authentication: Required (JWT token)
Path Parameters¶
user_id(required) - User's unique ObjectId
Request Body¶
Parameters:
outlet_ids(required) - Array of outlet IDs (replaces existing assignments)
Response¶
Returns updated user object with new outlet assignments.
Access Control¶
- SUPER_ADMIN: Can assign users to any outlet
- TENANT_ADMIN: Can assign users to any outlet within their tenant
- OUTLET_MANAGER: Can only assign/remove users to/from their managed outlets
- STAFF: Cannot modify outlet assignments
Business Rules¶
- Complete Replacement: This endpoint replaces ALL existing outlet assignments
- Tenant Validation: All outlets must belong to user's tenant
- Manager Restrictions:
- OUTLET_MANAGER can only assign to outlets they manage
- OUTLET_MANAGER can only modify users already in their outlets
- Subscription Limits: Assignment checks staff limits per outlet
- Empty Array: Setting
outlet_ids: []removes all outlet assignments
Subscription Enforcement¶
When assigning users to outlets:
for outlet_id in outlet_assignment.outlet_ids:
# Count current staff in outlet
staff_count = await count_staff_in_outlet(outlet_id)
subscription = await get_outlet_subscription(outlet_id)
# Check limits based on plan
if subscription.plan_type == "FREE" and staff_count >= 5:
raise HTTPException(403, "Outlet has reached FREE plan staff limit (5)")
elif subscription.plan_type == "PRO" and staff_count >= 50:
raise HTTPException(403, "Outlet has reached PRO plan staff limit (50)")
Examples¶
Assign to multiple outlets:
curl -X PUT "http://localhost:8000/api/v1/users/60f1b2b3b3b3b3b3b3b3b3b3/outlets" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"outlet_ids": ["507f1f77bcf86cd799439012", "507f1f77bcf86cd799439013"]
}'
Remove all outlet assignments:
curl -X PUT "http://localhost:8000/api/v1/users/60f1b2b3b3b3b3b3b3b3b3b3/outlets" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"outlet_ids": []
}'
Error Responses¶
403 Forbidden - Subscription limit exceeded:
{
"detail": "Outlet 507f1f77bcf86cd799439012 has reached FREE plan staff limit (5/5). Upgrade to add more staff.",
"error_code": "OUTLET_STAFF_LIMIT_EXCEEDED"
}
403 Forbidden - Manager permission denied:
404 Not Found - Invalid outlet:
Get User Statistics¶
Retrieve comprehensive user analytics and statistics based on access level.
Endpoint¶
Authentication: Required (JWT token)
Response¶
{
"total_users": 127,
"active_users": 119,
"locked_users": 3,
"users_by_role": {
"SUPER_ADMIN": 2,
"TENANT_ADMIN": 5,
"OUTLET_MANAGER": 15,
"STAFF": 105
},
"recent_signups": 8,
"subscription_usage": {
"plan_type": "PRO",
"staff_limit_per_outlet": 50,
"outlets_with_limits": [
{
"outlet_id": "507f1f77bcf86cd799439012",
"outlet_name": "Downtown Spa",
"current_staff": 45,
"limit": 50,
"percentage": 90,
"status": "approaching_limit"
}
]
}
}
Access Control¶
- SUPER_ADMIN: System-wide stats (all tenants or specific tenant context)
- TENANT_ADMIN: Stats for their tenant only
- OUTLET_MANAGER: Stats for users in their managed outlets
- STAFF: Cannot access user statistics
Statistics Included¶
- Total Users: Count of all users in accessible scope
- Active Users: Users with
is_active: true - Locked Users: Users with
is_locked: true(security monitoring) - Users by Role: Breakdown of user distribution across roles
- Recent Signups: New users in last 30 days
- Subscription Usage: Staff count vs limits per outlet
Examples¶
curl -X GET "http://localhost:8000/api/v1/users/stats/summary" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Use Cases¶
- Dashboard Analytics: Display user growth and distribution
- Capacity Planning: Monitor staff limits approaching subscription caps
- Security Monitoring: Track locked accounts and inactive users
- Subscription Optimization: Identify outlets near capacity for upgrade recommendations
Best Practices¶
For User Creation¶
✅ DO:
- Always validate email uniqueness before creation
- Check subscription limits before creating STAFF users
- Use auto-generated passwords for initial setup
- Force password change for auto-generated credentials
- Assign users to specific outlets for proper access control
- Send welcome emails with login instructions
❌ DON'T:
- Create users without outlet assignments (unless admins)
- Bypass role hierarchy restrictions
- Skip subscription limit checks
- Allow STAFF users to create other users
- Create users in outlets from other tenants
For User Updates¶
✅ DO:
- Validate outlet assignments belong to user's tenant
- Check role hierarchy before role changes
- Preserve audit fields (created_at, id)
- Use partial updates (only send changed fields)
- Re-validate subscription limits when adding outlets
❌ DON'T:
- Allow cross-tenant user modifications
- Permit role escalation beyond hierarchy
- Let STAFF modify security fields
- Bypass outlet manager restrictions
- Update deleted/inactive users without reactivation
For Password Management¶
✅ DO:
- Use secure password hashing (bcrypt)
- Generate cryptographically random temporary passwords
- Force password change for temporary credentials
- Reset failed login attempts on password reset
- Unlock accounts during password reset
- Log all password reset actions
❌ DON'T:
- Send passwords in plain text (except secure initial delivery)
- Allow password reset without authentication
- Skip password complexity validation
- Allow self-reset via admin endpoint
- Reuse old passwords
For Subscription Limits¶
✅ DO:
- Check limits before creating staff users
- Validate limits before outlet assignments
- Return clear upgrade recommendations
- Count only active STAFF toward limits
- Provide limit details in error messages
❌ DON'T:
- Count admins toward staff limits
- Allow limit bypass without validation
- Create users exceeding plan capacity
- Hide subscription limit information
Troubleshooting¶
Cannot Create User¶
Symptoms: User creation fails with validation error
Checks:
- Verify email is unique:
db.users.find_one({"email": "..."}) - Check subscription limits for tenant
- Verify outlets exist and belong to tenant
- Confirm role hierarchy allows creation
- Validate outlet manager has access to assigned outlets
Fix:
- Use different email address
- Upgrade subscription plan if at limit
- Assign to valid outlets within tenant
- Adjust role to match hierarchy
User Cannot Access Resources¶
Symptoms: 403 Forbidden errors when accessing features
Checks:
- Verify user has correct role:
GET /users/{user_id} - Check outlet assignments:
user.outlet_ids - Confirm tenant membership:
user.tenant_ids - Validate account is active:
user.is_active: true - Check account not locked:
user.is_locked: false
Fix:
- Update outlet assignments if incorrect
- Promote role if needed (with proper permissions)
- Reactivate account if deactivated
- Unlock account via password reset
Subscription Limit Errors¶
Symptoms: Cannot create users, "limit exceeded" errors
Checks:
- Get current subscription:
GET /subscriptions/current - Count staff per outlet:
db.users.count_documents({"role": "STAFF", "outlet_ids": outlet_id}) - Review plan limits: FREE=5, PRO=50, ENTERPRISE=unlimited
- Check if counting includes inactive users
Fix:
- Upgrade subscription plan
- Deactivate/delete unused staff accounts
- Redistribute staff across multiple outlets
- Contact support for limit increase
Outlet Assignment Failures¶
Symptoms: Cannot assign users to outlets
Checks:
- Verify outlet exists:
GET /outlets/{outlet_id} - Check outlet belongs to user's tenant
- Confirm manager has access to outlet (if OUTLET_MANAGER)
- Validate subscription limits for outlet
Fix:
- Use outlets from correct tenant
- Request manager access to outlet
- Upgrade plan if outlet at capacity
- Create user in different outlet
API Reference Summary¶
| Endpoint | Method | Purpose | Access |
|---|---|---|---|
/users |
GET | List users with filtering | OUTLET_MANAGER+ |
/users |
POST | Create new user | OUTLET_MANAGER+ |
/users/me |
GET | Get own profile | All authenticated |
/users/{user_id} |
GET | Get user by ID | Role-based |
/users/{user_id} |
PUT | Update user | Role-based |
/users/{user_id} |
DELETE | Soft delete user | OUTLET_MANAGER+ |
/users/{user_id}/reset-password |
POST | Reset password | OUTLET_MANAGER+ |
/users/{user_id}/outlets |
PUT | Update outlets | TENANT_ADMIN+ |
/users/stats/summary |
GET | User statistics | OUTLET_MANAGER+ |
Security Considerations¶
Authentication & Authorization¶
- All endpoints require valid JWT token
- Role hierarchy strictly enforced
- Tenant isolation on every request
- Outlet scoping for managers
- Session validation on each request
Password Security¶
- Bcrypt hashing with salt rounds ≥12
- Temporary passwords auto-generated (cryptographically secure)
- Failed login attempt tracking (max 5 attempts)
- Account lockout after failed attempts (30 minute duration)
- Password change timestamps for audit
Data Protection¶
- Soft deletion preserves audit trail
- Sensitive fields excluded from logs
- Role-based field access restrictions
- Tenant data isolation enforced at DB level
- No cross-tenant data exposure
Audit & Compliance¶
- All user actions logged with timestamps
- Password reset actions tracked with admin ID
- Role changes require administrative approval
- Deletion preserves historical data
- GDPR-compliant data retention policies
Next Steps:
- Review role hierarchy and permissions: Role Hierarchy
- Check subscription limits for your plan: Subscription Limits
- Create your first user:
POST /users - Assign outlets:
PUT /users/{user_id}/outlets - Monitor usage:
GET /users/stats/summary
For subscription management and upgrades, refer to the Subscription Management documentation.