Stitchflow
Strapi logo

Strapi User Management API Guide

API workflow

How to automate user lifecycle operations through APIs with caveats that matter in production.

UpdatedMar 16, 2026

Summary and recommendation

Strapi exposes two entirely separate REST user APIs that must not be conflated in an identity graph: the Users & Permissions plugin API at /api/users (end-users, JWT or API Token auth) and the admin panel API at /admin/users (admin panel accounts, admin JWT only).

Authentication flows, permission models, token lifetimes, and response shapes differ between them.

The /api/users endpoint returns a plain array rather than Strapi's standard { data, meta } paginated wrapper, which breaks generic Strapi client assumptions.

Pagination on /api/users uses either page-based (pagination[page] / pagination[pageSize]) or offset-based (pagination[start] / pagination[limit]) parameters, with a default page size of 25 and a maximum of 100.

API quick reference

Has user APIYes
Auth methodJWT Bearer token (Users & Permissions plugin) or API Token (admin-generated static token)
Base URLOfficial docs
SCIM availableNo
SCIM plan requiredEnterprise

Authentication

Auth method: JWT Bearer token (Users & Permissions plugin) or API Token (admin-generated static token)

Setup steps

  1. POST credentials to /api/auth/local to receive a JWT token.
  2. Include the JWT in subsequent requests as: Authorization: Bearer .
  3. Alternatively, generate an API Token in the Strapi Admin Panel under Settings > API Tokens and pass it as Authorization: Bearer .
  4. For admin-panel endpoints (/admin/*), authenticate via the admin JWT obtained from POST /admin/login.

Required scopes

Scope Description Required for
plugin::users-permissions.user.find Read access to the users collection via the Users & Permissions plugin. GET /api/users
plugin::users-permissions.user.findOne Read a single user record. GET /api/users/:id
plugin::users-permissions.user.create Create a new user. POST /api/users
plugin::users-permissions.user.update Update an existing user. PUT /api/users/:id
plugin::users-permissions.user.destroy Delete a user. DELETE /api/users/:id
plugin::users-permissions.user.me Read the currently authenticated user's own profile. GET /api/users/me

User object / data model

Field Type Description On create On update Notes
id integer Auto-incremented unique identifier. auto immutable Primary key.
username string Unique username. required optional Must be unique across users.
email string User email address. required optional Must be unique; used for login.
password string Hashed password. required optional Never returned in API responses.
provider string Auth provider (e.g., local, google). auto read-only Defaults to 'local'.
confirmed boolean Whether the user's email is confirmed. auto (false) optional Set to true to bypass email confirmation.
blocked boolean Whether the user is blocked from logging in. auto (false) optional Blocked users cannot authenticate.
role relation (Role) Assigned role object. optional optional Defaults to 'Authenticated' role if not specified.
createdAt datetime Record creation timestamp. auto immutable ISO 8601 format.
updatedAt datetime Last update timestamp. auto auto ISO 8601 format.
resetPasswordToken string Token used for password reset flow. null system-managed Not returned in standard API responses.
confirmationToken string Token used for email confirmation. system-managed system-managed Not returned in standard API responses.

Core endpoints

Register a new user (public)

  • Method: POST
  • URL: /api/auth/local/register
  • Watch out for: Email confirmation may be required depending on plugin settings; unconfirmed users cannot log in if confirmation is enforced.

Request example

POST /api/auth/local/register
{
  "username": "jdoe",
  "email": "jdoe@example.com",
  "password": "Str0ngPass!"
}

Response example

{
  "jwt": "eyJhbGci...",
  "user": {
    "id": 1,
    "username": "jdoe",
    "email": "jdoe@example.com",
    "confirmed": false,
    "blocked": false
  }
}

Authenticate (login)

  • Method: POST
  • URL: /api/auth/local
  • Watch out for: The 'identifier' field accepts either username or email.

Request example

POST /api/auth/local
{
  "identifier": "jdoe@example.com",
  "password": "Str0ngPass!"
}

Response example

{
  "jwt": "eyJhbGci...",
  "user": {
    "id": 1,
    "username": "jdoe",
    "email": "jdoe@example.com"
  }
}

Get current authenticated user

  • Method: GET
  • URL: /api/users/me
  • Watch out for: Returns the user associated with the JWT; requires a valid non-expired token.

Request example

GET /api/users/me
Authorization: Bearer <jwt>

Response example

{
  "id": 1,
  "username": "jdoe",
  "email": "jdoe@example.com",
  "confirmed": true,
  "blocked": false,
  "role": { "id": 1, "name": "Authenticated" }
}

List all users (admin/privileged)

  • Method: GET
  • URL: /api/users
  • Watch out for: This endpoint returns an array (not a paginated object wrapper). The role must have 'find' permission granted in the Users & Permissions plugin settings.

Request example

GET /api/users?pagination[page]=1&pagination[pageSize]=25
Authorization: Bearer <admin-jwt-or-api-token>

Response example

[
  {
    "id": 1,
    "username": "jdoe",
    "email": "jdoe@example.com",
    "confirmed": true,
    "blocked": false
  }
]

Get a single user

  • Method: GET
  • URL: /api/users/:id
  • Watch out for: Requires 'findOne' permission. Public access is disabled by default.

Request example

GET /api/users/1
Authorization: Bearer <admin-jwt-or-api-token>

Response example

{
  "id": 1,
  "username": "jdoe",
  "email": "jdoe@example.com",
  "confirmed": true,
  "blocked": false,
  "role": { "id": 1, "name": "Authenticated" }
}

Create a user (admin)

  • Method: POST
  • URL: /api/users
  • Watch out for: Password is stored hashed; role ID must correspond to an existing role.

Request example

POST /api/users
Authorization: Bearer <admin-jwt-or-api-token>
{
  "username": "newuser",
  "email": "new@example.com",
  "password": "Pass123!",
  "role": 1
}

Response example

{
  "id": 2,
  "username": "newuser",
  "email": "new@example.com",
  "confirmed": false,
  "blocked": false
}

Update a user

  • Method: PUT
  • URL: /api/users/:id
  • Watch out for: Uses PUT (full-style), but Strapi applies only the provided fields. Sending a new password here will hash and update it.

Request example

PUT /api/users/2
Authorization: Bearer <admin-jwt-or-api-token>
{
  "blocked": true
}

Response example

{
  "id": 2,
  "username": "newuser",
  "email": "new@example.com",
  "blocked": true
}

Delete a user

  • Method: DELETE
  • URL: /api/users/:id
  • Watch out for: Returns the deleted user object. Deletion is permanent; there is no soft-delete by default.

Request example

DELETE /api/users/2
Authorization: Bearer <admin-jwt-or-api-token>

Response example

{
  "id": 2,
  "username": "newuser",
  "email": "new@example.com"
}

Rate limits, pagination, and events

  • Rate limits: Strapi does not enforce built-in rate limits at the application layer by default. Rate limiting can be added via middleware (e.g., koa-ratelimit) or at the infrastructure/reverse-proxy level. Strapi Cloud may apply platform-level limits not publicly documented per-tier.

  • Rate-limit headers: No

  • Retry-After header: No

  • Rate-limit notes: No official per-tier rate-limit figures are published in Strapi documentation as of the policy date.

  • Pagination method: offset

  • Default page size: 25

  • Max page size: 100

  • Pagination pointer: pagination[page] / pagination[pageSize] (page-based) or pagination[start] / pagination[limit] (offset-based)

  • Webhooks available: Yes

  • Webhook notes: Strapi supports webhooks configured in the Admin Panel under Settings > Webhooks. Webhooks fire on content lifecycle events (create, update, delete, publish, unpublish) for content types. There are no dedicated user-lifecycle webhook events (e.g., user.created) exposed natively through the Admin Panel UI.

  • Alternative event strategy: Custom lifecycle hooks can be implemented in Strapi's server-side code (src/extensions/users-permissions/content-types/user/lifecycles.js) to trigger external calls on user create/update/delete events.

  • Webhook events: entry.create, entry.update, entry.delete, entry.publish, entry.unpublish, media.create, media.update, media.delete

SCIM API status

  • SCIM available: No
  • SCIM version: Not documented
  • Plan required: Enterprise
  • Endpoint: Not documented

Limitations:

  • Strapi does not provide a native SCIM 2.0 endpoint.
  • SSO (SAML/OIDC) is available on the Enterprise Edition plan.
  • Automated user provisioning via SCIM is not natively supported; custom middleware or third-party connectors would be required.

Common scenarios

Three automation scenarios are well-supported by the documented API surface.

First, programmatic user creation: POST /api/users with confirmed: true using a full-access API Token to create and activate an end-user in a single call, bypassing the email confirmation flow entirely

note this is intentional for machine-provisioned accounts but should not be used where email verification is a compliance requirement.

Second, account suspension: PUT /api/users/:id with { "blocked": true } sets the blocked field, causing subsequent /api/auth/local login attempts to return a 400;

critically, this does not invalidate JWTs already in circulation, so short token TTLs or a token denylist are required for immediate session termination.

Third, role assignment: the Users & Permissions plugin enforces a single role per user, so PUT /api/users/:userId with a new role ID replaces the existing role

there is no multi-role support natively, and role IDs must be retrieved first via GET /api/users-permissions/roles using an admin JWT.

Programmatically create and activate a new end-user

  1. Obtain an admin JWT via POST /api/auth/local with admin credentials, or use a full-access API Token.
  2. POST /api/users with { username, email, password, role, confirmed: true } to create the user and mark them as confirmed in one step.
  3. Verify creation by checking the returned user object's 'id' and 'confirmed' fields.
  4. Optionally assign a specific role by passing the role's numeric ID in the 'role' field.

Watch out for: Setting confirmed: true bypasses the email confirmation flow. If your Strapi instance enforces email confirmation, omitting this will prevent the user from logging in until they confirm.

Block (suspend) a user account

  1. Authenticate with an admin JWT or full-access API Token.
  2. Identify the user's numeric ID via GET /api/users?filters[email][$eq]=target@example.com.
  3. PUT /api/users/:id with { "blocked": true }.
  4. The user's subsequent login attempts to /api/auth/local will return a 400 error indicating the account is blocked.

Watch out for: Blocking does not invalidate existing JWTs already issued to the user. Implement token revocation logic (e.g., short TTLs or a token denylist) if immediate session termination is required.

Assign a role to an existing user

  1. Retrieve available roles via GET /api/users-permissions/roles (requires admin JWT).
  2. Note the numeric 'id' of the target role.
  3. PUT /api/users/:userId with { "role": }.
  4. Confirm the update by fetching GET /api/users/:userId and checking the 'role' relation.

Watch out for: The Users & Permissions plugin supports a single role per user. Assigning a new role replaces the existing one; there is no multi-role support natively.

Why building this yourself is a trap

The most consequential integration trap is the dual-database architecture: end-users at /api/users and admin panel users at /admin/users are stored separately, authenticated separately, and cannot be queried interchangeably. Any identity graph that conflates the two will produce incorrect access state.

A second trap is permission grant visibility: even authenticated users cannot reach /api/users endpoints unless the role's permissions are explicitly enabled in Admin Panel > Settings > Users & Permissions Plugin > Roles - this is a runtime configuration dependency, not a code-level one, and it silently returns 403 without a descriptive error.

SCIM 2.0 is not natively available on any plan; automated provisioning against an IdP requires custom middleware or a third-party connector, and there is no published roadmap for native SCIM support.

Finally, Strapi v4 and v5 have divergent plugin structures, so endpoint behavior and response shapes should always be validated against the installed version before building automation.

Automate Strapi workflows without one-off scripts

Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.

Every app coverage, including apps without APIs
60+ app integrations plus browser automation for apps without APIs
IT graph reconciliation across apps and your IdP
Less than a week to launch, maintained as APIs and admin consoles change
SOC 2 Type II. ~2 hours of your team's time

UpdatedMar 16, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

Abnormal Security logo

Abnormal Security

API Only
AutomationAPI only
Last updatedMar 2026

Abnormal Security is an enterprise email security platform focused on detecting and investigating threats such as phishing, account takeover (ATO), and vendor email compromise. It does not support SCIM provisioning, which means every app in your stack

ActiveCampaign logo

ActiveCampaign

API Only
AutomationAPI only
Last updatedFeb 2026

ActiveCampaign uses a group-based permission model: every user belongs to exactly one group, and all feature-area access (Contacts, Campaigns, Automations, Deals, Reports, Templates) is configured at the group level, not per individual. The default Adm

ADP logo

ADP

API Only
AutomationAPI only
Last updatedFeb 2026

ADP Workforce Now is a mid-market to enterprise HCM platform that serves as the HR source of record for employee data — payroll, benefits, time, and talent. User access is governed by a hybrid permission model: predefined security roles (Security Maste