Users & Permissions REST API
Page summary:
The Users & Permissions feature provides REST API endpoints for authentication, user management, roles, and permissions.
The Users & Permissions feature exposes a set of REST API endpoints for authentication, user management, and role/permission management. These endpoints are separate from the standard content-type CRUD endpoints and have their own response shapes. For a general overview and configuration options, see the Users & Permissions introduction.
All endpoints use the /api prefix. For example, if your Strapi server runs at http://localhost:1337, the login endpoint is http://localhost:1337/api/auth/local.
Authentication
Authentication endpoints handle login, registration, and password management. Most of these endpoints are public by default and do not require a Bearer token.
Login
POST /api/auth/local
Authenticates a user with their identifier (email or username) and password, returning a JWT and the user object.
curl -X POST http://localhost:1337/api/auth/local \
-H "Content-Type: application/json" \
-d '{"identifier": "user@example.com", "password": "yourPassword"}'
{
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"documentId": "x74detpqybxw0bn6ormua5g2",
"username": "testuser1",
"email": "user@example.com",
"provider": "local",
"confirmed": true,
"blocked": false,
"createdAt": "2024-03-15T10:00:00.000Z",
"updatedAt": "2024-03-15T10:00:00.000Z",
"publishedAt": "2024-03-15T10:00:00.000Z"
}
}
Possible errors:
| Status | Message | Cause |
|---|---|---|
| 400 | "Invalid identifier or password" | Wrong credentials |
| 400 | "Your account email is not confirmed" | Email confirmation required but not completed |
| 400 | "Your account has been blocked by an administrator" | Account blocked |
This endpoint is rate limited (default: 5 requests per 5 minutes).
Register
POST /api/auth/local/register
Creates a new user account and returns a JWT along with the user object.
curl -X POST http://localhost:1337/api/auth/local/register \
-H "Content-Type: application/json" \
-d '{"username": "newuser", "email": "newuser@example.com", "password": "Password123!"}'
{
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 2,
"documentId": "xjpaytstw1gm7wdfoc6c2k13",
"username": "newuser",
"email": "newuser@example.com",
"provider": "local",
"confirmed": true,
"blocked": false,
"createdAt": "2024-03-15T10:00:00.000Z",
"updatedAt": "2024-03-15T10:00:00.000Z",
"publishedAt": "2024-03-15T10:00:00.000Z"
}
}
Only username, email, and password are accepted in the request body by default. To allow additional fields (e.g., a fullName field you added to the User content-type), you must explicitly list them in the register.allowedFields configuration. See Registration configuration for details.
When email confirmation is enabled, the response contains only the user object without a JWT. The user must confirm their email before they can log in.
The newly registered user is assigned the default role, which is "Authenticated" unless changed in Settings > Users & Permissions > Advanced Settings.
Possible errors:
| Status | Message | Cause |
|---|---|---|
| 400 | "Email or Username are already taken" | Duplicate email or username |
| 400 | "Invalid parameters: fieldName" | Extra fields not listed in allowedFields |
| 400 | "Register action is currently disabled" | Registration disabled in admin settings |
This endpoint is rate limited.
Forgot password
POST /api/auth/forgot-password
Sends a password reset email to the specified address. Requires an email provider to be configured.
curl -X POST http://localhost:1337/api/auth/forgot-password \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com"}'
{
"ok": true
}
This endpoint always returns { "ok": true } regardless of whether the email address exists in the system. This is intentional to prevent user enumeration attacks.
This endpoint is rate limited.
Reset password
POST /api/auth/reset-password
Resets a user's password using a token received by email. The code, password, and passwordConfirmation fields are all required.
curl -X POST http://localhost:1337/api/auth/reset-password \
-H "Content-Type: application/json" \
-d '{"code": "resetTokenFromEmail", "password": "NewPassword123!", "passwordConfirmation": "NewPassword123!"}'
{
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"documentId": "x74detpqybxw0bn6ormua5g2",
"username": "testuser1",
"email": "user@example.com",
"provider": "local",
"confirmed": true,
"blocked": false,
"createdAt": "2024-03-15T10:00:00.000Z",
"updatedAt": "2024-03-15T10:00:00.000Z",
"publishedAt": "2024-03-15T10:00:00.000Z"
}
}
Possible errors:
| Status | Message | Cause |
|---|---|---|
| 400 | "passwordConfirmation is a required field" | Missing passwordConfirmation |
| 400 | "Passwords do not match" | password and passwordConfirmation differ |
| 400 | "Incorrect code provided" | Invalid or expired reset token |
This endpoint is rate limited.
Change password
POST /api/auth/change-password
Changes the password for the currently authenticated user. Requires a valid Bearer token. The currentPassword, password, and passwordConfirmation fields are all required.
curl -X POST http://localhost:1337/api/auth/change-password \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{"currentPassword": "OldPassword123!", "password": "NewPassword456!", "passwordConfirmation": "NewPassword456!"}'
{
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"documentId": "x74detpqybxw0bn6ormua5g2",
"username": "testuser1",
"email": "user@example.com",
"provider": "local",
"confirmed": true,
"blocked": false,
"createdAt": "2024-03-15T10:00:00.000Z",
"updatedAt": "2024-03-15T10:00:00.000Z",
"publishedAt": "2024-03-15T10:00:00.000Z"
}
}
Possible errors:
| Status | Message | Cause |
|---|---|---|
| 400 | "passwordConfirmation is a required field" | Missing passwordConfirmation |
| 400 | "Passwords do not match" | password and passwordConfirmation differ |
| 400 | "The provided current password is invalid" | Wrong current password |
| 400 | "Your new password must be different than your current password" | Same password reused |
This endpoint is rate limited.
Email confirmation
GET /api/auth/email-confirmation
Confirms a user's email address using a token from the confirmation email.
Query parameter: confirmation (the token received in the confirmation email).
curl -G http://localhost:1337/api/auth/email-confirmation \
--data-urlencode "confirmation=confirmationTokenHere"
After confirming the email, Strapi redirects the user to the URL configured in the admin panel under Settings > Users & Permissions > Advanced Settings > "Redirection url".
Possible errors:
| Status | Message | Cause |
|---|---|---|
| 400 | "confirmation is a required field" | confirmation query parameter missing |
| 400 | "Invalid token" | Confirmation token malformed, expired, or already used |
Send email confirmation
POST /api/auth/send-email-confirmation
Resends the confirmation email to a user who has not yet confirmed their email address.
curl -X POST http://localhost:1337/api/auth/send-email-confirmation \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com"}'
{
"email": "user@example.com",
"sent": true
}
Possible errors:
| Status | Message | Cause |
|---|---|---|
| 400 | "Already confirmed" | User already confirmed their email |
| 400 | "User blocked" | Account blocked |
This endpoint is rate limited.
Provider authentication
GET /api/connect/:provider
Redirects the user to a third-party provider's login page (e.g., Google, GitHub, Discord). Replace :provider with the provider name. See Setting up providers for configuration instructions.
Provider callback
GET /api/auth/:provider/callback
Handles the OAuth callback after the user authenticates with a third-party provider. On success, returns the same response shape as login (jwt + user).
If the username derived from the provider profile already exists, a unique username is generated automatically to avoid conflicts.
Session management
When session management is enabled (jwtManagement: 'refresh' in the plugin configuration), these additional endpoints become available. They return 404 when the default legacy JWT mode is active.
Refresh token
POST /api/auth/refresh
Exchanges a refresh token for a new access token. The old refresh token is invalidated and a new one is returned (token rotation).
curl -X POST http://localhost:1337/api/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refreshToken": "yourRefreshToken"}'
{
"jwt": "newAccessToken",
"refreshToken": "newRefreshToken"
}
When httpOnly is enabled in the session configuration, the new refresh token is set as an HTTP-only cookie instead of being included in the response body. In that case, the response only contains { "jwt": "newAccessToken" }.
List sessions
GET /api/auth/sessions
Returns the active sessions of the authenticated user, with the current session first and the other sessions from the most to the least recently used. Requires a valid Bearer token.
curl http://localhost:1337/api/auth/sessions \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"data": [
{
"id": "session-current",
"deviceId": "device-a",
"deviceName": "Chrome",
"current": true,
"loginAt": "2026-06-10T08:00:00.000Z",
"lastActiveAt": "2026-06-11T10:00:00.000Z"
},
{
"id": "session-other",
"deviceId": "device-b",
"deviceName": "Safari",
"current": false,
"loginAt": "2026-06-09T08:00:00.000Z",
"lastActiveAt": "2026-06-10T09:00:00.000Z"
}
]
}
Each session of the response contains the following fields:
| Field | Description |
|---|---|
id | Identifier of the session. |
deviceId | (optional) Identifier of the device associated with the session. |
deviceName | (optional) Device name, derived from the user agent sent at login. |
current | true for the session used to send the request. |
loginAt | (optional) Date of the login, in the ISO 8601 format. |
lastActiveAt | (optional) Date of the latest refresh token rotation, in the ISO 8601 format. It is not updated on every API request. |
Sessions never include tokens, nor database identifiers other than the session and device identifiers.
Possible errors:
| Status | Message | Cause |
|---|---|---|
| 401 | "Missing authentication" | Missing or invalid Bearer token |
Revoke a session
DELETE /api/auth/sessions/:sessionId
Revokes a session of the authenticated user. Requires a valid Bearer token.
curl -X DELETE http://localhost:1337/api/auth/sessions/session-other \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"data": {}
}
Possible errors:
| Status | Message | Cause |
|---|---|---|
| 401 | "Missing authentication" | Missing or invalid Bearer token |
| 404 | "Session not found" | The session does not exist or belongs to another user |
Logout
POST /api/auth/logout
Revokes the current session of the authenticated user. Requires a valid Bearer token.
The optional request body widens the scope of the revocation:
| Body | Revoked sessions |
|---|---|
{ "scope": "all" } | All the sessions of the user. |
{ "deviceId": "device-a" } | All the sessions of the given device. |
The current session is read from the request context, then from the refresh token found in the cookie or in the refreshToken property of the request body. If the current session cannot be identified, all the sessions of the user are revoked.
curl -X POST http://localhost:1337/api/auth/logout \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"ok": true
}
When httpOnly is enabled in the session configuration, the refresh token cookie is also cleared.
Users
User management endpoints handle CRUD operations on end-user records. Each endpoint requires the corresponding permission to be enabled for the user's role in the admin panel (Settings > Users & Permissions > Roles).
User endpoints return bare JSON objects (not wrapped in a data key), unlike standard Strapi content-type endpoints.
Get current user
GET /api/users/me
Returns the user associated with the provided JWT. Requires authentication.
curl http://localhost:1337/api/users/me \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"id": 1,
"documentId": "x74detpqybxw0bn6ormua5g2",
"username": "testuser1",
"email": "user@example.com",
"provider": "local",
"confirmed": true,
"blocked": false,
"createdAt": "2024-03-15T10:00:00.000Z",
"updatedAt": "2024-03-15T10:00:00.000Z",
"publishedAt": "2024-03-15T10:00:00.000Z"
}
List users
GET /api/users
Returns a list of users. Supports filters, sort, and pagination query parameters.
curl http://localhost:1337/api/users \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Example response (200): An array of user objects.
Get a user
GET /api/users/:id
Returns a single user by their integer id.
curl http://localhost:1337/api/users/1 \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Example response (200): A user object.
Count users
GET /api/users/count
Returns the total number of users. Supports the filters query parameter.
curl http://localhost:1337/api/users/count \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
42
Create a user
POST /api/users
Creates a new user. If role is omitted, the default role is assigned.
curl -X POST http://localhost:1337/api/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{"username": "newuser", "email": "newuser@example.com", "password": "Password123!", "role": 1, "confirmed": true}'
Example response (201): A user object with the populated role.
The role field accepts both numeric id (legacy) and documentId (Strapi v5 convention). Examples:
"role": 1— numeric ID"role": "x74detpqybxw0bn6ormua5g2"— documentId
Update a user
PUT /api/users/:id
Updates an existing user. Only include the fields you want to change.
curl -X PUT http://localhost:1337/api/users/1 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{"username": "updateduser"}'
Example response (200): The updated user object.
Possible errors:
| Status | Message | Cause |
|---|---|---|
| 404 | "User not found" | No user with the given id |
| 400 | "Username already taken" or "Email already taken" | Duplicate value |
documentId and numeric IDsWhen updating a user, including the role relation, you can use either documentId (recommended for Strapi 5) or numeric id in relation payloads:
{
"role": "x74detpqybxw0bn6ormua5g2" // role documentId (shorthand)
}
or
{
"role": {
"connect": ["x74detpqybxw0bn6ormua5g2"] // role documentId (longhand)
}
}
or
{
"role": 1 // numeric ID (legacy)
}
For custom relations, you can also use the longhand format. Both formats are accepted for backward compatibility.
Delete a user
DELETE /api/users/:id
Deletes a user by their integer id.
curl -X DELETE http://localhost:1337/api/users/1 \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Example response (200): The deleted user object.
Roles
Role endpoints manage end-user roles and their associated permissions. These endpoints use the /api/users-permissions/ prefix, not the standard /api/ prefix used by content-type endpoints.
List roles
GET /api/users-permissions/roles
Returns all available roles with user counts.
curl http://localhost:1337/api/users-permissions/roles \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"roles": [
{
"id": 1,
"documentId": "abcdefghijklmnop12345678",
"name": "Authenticated",
"description": "Default role given to authenticated user.",
"type": "authenticated",
"createdAt": "2024-03-15T10:00:00.000Z",
"updatedAt": "2024-03-15T10:00:00.000Z",
"publishedAt": "2024-03-15T10:00:00.000Z",
"nb_users": 2
},
{
"id": 2,
"documentId": "qrstuvwxyzabcdef98765432",
"name": "Public",
"description": "Default role given to unauthenticated user.",
"type": "public",
"createdAt": "2024-03-15T10:00:00.000Z",
"updatedAt": "2024-03-15T10:00:00.000Z",
"publishedAt": "2024-03-15T10:00:00.000Z",
"nb_users": 0
}
]
}
Get a role
GET /api/users-permissions/roles/:id
Returns a single role by id, including its full permissions tree.
curl http://localhost:1337/api/users-permissions/roles/1 \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Example response (200): An object of the form { "role": { ... } }, where the role contains a nested permissions structure mapping plugins to controllers to actions.
Create a role
POST /api/users-permissions/roles
Creates a new role.
curl -X POST http://localhost:1337/api/users-permissions/roles \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{"name": "Editor", "description": "Can edit content", "type": "editor"}'
{
"ok": true
}
Update a role
PUT /api/users-permissions/roles/:id
Updates an existing role.
curl -X PUT http://localhost:1337/api/users-permissions/roles/1 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{"description": "Updated description"}'
{
"ok": true
}
Delete a role
DELETE /api/users-permissions/roles/:id
Deletes a role by id. The Public role cannot be deleted. When a role is deleted, users assigned to it are reassigned to the Public role.
curl -X DELETE http://localhost:1337/api/users-permissions/roles/3 \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"ok": true
}
Possible errors:
| Status | Message | Cause |
|---|---|---|
| 400 | "Cannot delete public role" | Attempted to delete the Public role |
Permissions
The permissions endpoint returns the complete permission tree, showing which actions are available across all plugins and content-types.
List permissions
GET /api/users-permissions/permissions
Returns a nested object mapping each plugin name to a controllers object, which itself maps controller names to actions.
curl http://localhost:1337/api/users-permissions/permissions \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Example response (200): A nested permissions object where top-level keys are plugin names, each containing a controllers object whose keys are controller names, and each controller maps to actions with their enabled status and policy information.