Stitchflow
GitLab logo

GitLab User Management API Guide

API workflow

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

UpdatedMar 9, 2026

Summary and recommendation

GitLab exposes a REST API at `https://gitlab.com/api/v4` with full user lifecycle coverage: create, read, update, block, deactivate, and delete. All write operations and full user detail responses (including email) require a Personal Access Token or OAuth 2.0 token belonging to an admin user - non-admin tokens receive 403 on writes and redacted responses on reads.

User state transitions (block, unblock, activate, deactivate) are not settable via `PUT /users/:id`; each state has a dedicated endpoint (`/block`, `/unblock`, `/activate`, `/deactivate`), and conflating them is a common integration error.

For identity graph construction, the user object exposes `id`, `username`, `email` (admin-only), `state`, `created_at`, and external identity linkages via SCIM's `externalId` field. Pagination uses offset-based `page` and `per_page` params (max 100); the `X-Total` header may be absent on GitLab.com for large result sets, so Link header navigation is the reliable traversal path.

GitLab.com enforces 2,000 authenticated requests per minute at the general API level, with stricter per-endpoint limits on the Users API in some contexts.

API quick reference

Has user APIYes
Auth methodPersonal Access Token (PAT), OAuth 2.0, or Session cookie. PAT is most common for server-to-server; OAuth 2.0 for user-delegated access.
Base URLOfficial docs
SCIM availableYes
SCIM plan requiredPremium or Ultimate (GitLab.com groups); requires Group SSO (SAML) to be configured first.

Authentication

Auth method: Personal Access Token (PAT), OAuth 2.0, or Session cookie. PAT is most common for server-to-server; OAuth 2.0 for user-delegated access.

Setup steps

  1. Personal Access Token: Navigate to GitLab > User Settings > Access Tokens. Create a token with required scopes (e.g., 'api', 'read_user'). Pass as header: 'PRIVATE-TOKEN: '.
  2. OAuth 2.0: Register an application at GitLab > User Settings > Applications. Obtain client_id and client_secret. Implement Authorization Code or Client Credentials flow. Pass Bearer token as 'Authorization: Bearer '.
  3. Admin-level operations (create/delete users) require a PAT or OAuth token belonging to an admin user.

Required scopes

Scope Description Required for
api Full read/write access to the authenticated user's API, including all groups and projects. Creating, updating, deleting users; all write operations.
read_user Read-only access to the authenticated user's profile via /user endpoint. Reading current user profile only.
read_api Read-only access to the API, including all groups, projects, and user listings. Listing and reading user data without write access.
admin_mode Allows API calls to be made in admin mode (GitLab 15.8+, self-managed only). Admin-level user management on self-managed instances.

User object / data model

Field Type Description On create On update Notes
id integer Unique user ID. auto-assigned immutable Used as path param in most endpoints.
username string Unique username (login handle). required optional Must be unique across instance.
email string Primary email address. required optional Must be unique. Admin-only field on read for other users.
name string Full display name. required optional
password string User password. optional (required if no reset_password) optional Write-only; never returned in responses.
reset_password boolean Send password reset email on creation. optional n/a
state string User state: active, blocked, deactivated. auto (active) via dedicated block/unblock endpoints Cannot be set directly via PUT; use /block, /unblock, /activate, /deactivate.
avatar_url string URL to user avatar image. optional optional
web_url string URL to user's GitLab profile. auto-assigned immutable
created_at datetime (ISO 8601) Account creation timestamp. auto-assigned immutable
bio string User biography. optional optional
location string User location. optional optional
public_email string Publicly visible email. optional optional
skype string Skype ID. optional optional
linkedin string LinkedIn profile. optional optional
twitter string Twitter handle. optional optional
admin boolean Whether user has admin privileges. optional optional Admin-only field; requires admin token to set.
external boolean Marks user as external (restricted access). optional optional External users cannot access internal/private projects by default.
identities array List of external identity providers linked to user. optional optional Each entry has provider and extern_uid fields.
two_factor_enabled boolean Whether 2FA is enabled. read-only read-only Admin can disable via DELETE /users/:id/two_factor.

Core endpoints

List users

  • Method: GET
  • URL: /api/v4/users
  • Watch out for: Full user details (including email) are only returned when called by an admin token. Non-admin tokens return a limited public profile.

Request example

GET /api/v4/users?search=john&active=true&per_page=50
PRIVATE-TOKEN: <admin_token>

Response example

[
  {
    "id": 1,
    "username": "john_doe",
    "name": "John Doe",
    "state": "active",
    "email": "john@example.com",
    "created_at": "2023-01-15T10:00:00.000Z"
  }
]

Get single user

  • Method: GET
  • URL: /api/v4/users/:id
  • Watch out for: Non-admin callers receive a reduced response without email or admin fields.

Request example

GET /api/v4/users/42
PRIVATE-TOKEN: <admin_token>

Response example

{
  "id": 42,
  "username": "jane_doe",
  "name": "Jane Doe",
  "email": "jane@example.com",
  "state": "active",
  "admin": false,
  "two_factor_enabled": true
}

Create user

  • Method: POST
  • URL: /api/v4/users
  • Watch out for: Requires admin token. Either password or reset_password:true must be provided. Email confirmation may be required depending on instance settings.

Request example

POST /api/v4/users
PRIVATE-TOKEN: <admin_token>
Content-Type: application/json

{"email":"new@example.com","username":"newuser","name":"New User","reset_password":true}

Response example

{
  "id": 99,
  "username": "newuser",
  "name": "New User",
  "email": "new@example.com",
  "state": "active",
  "created_at": "2024-06-01T12:00:00.000Z"
}

Update user

  • Method: PUT
  • URL: /api/v4/users/:id
  • Watch out for: Requires admin token. Cannot change state via this endpoint; use /block or /deactivate instead.

Request example

PUT /api/v4/users/99
PRIVATE-TOKEN: <admin_token>
Content-Type: application/json

{"name":"Updated Name","email":"updated@example.com"}

Response example

{
  "id": 99,
  "username": "newuser",
  "name": "Updated Name",
  "email": "updated@example.com",
  "state": "active"
}

Delete user

  • Method: DELETE
  • URL: /api/v4/users/:id
  • Watch out for: hard_delete=true permanently removes the user and all associated records. Default (false) blocks the user and moves contributions to a ghost user. Irreversible if hard_delete=true.

Request example

DELETE /api/v4/users/99?hard_delete=false
PRIVATE-TOKEN: <admin_token>

Response example

HTTP 204 No Content

Block user

  • Method: POST
  • URL: /api/v4/users/:id/block
  • Watch out for: Blocked users cannot log in but their data is preserved. Returns 201 on success, 403 if trying to block an admin, 404 if user not found.

Request example

POST /api/v4/users/99/block
PRIVATE-TOKEN: <admin_token>

Response example

HTTP 201 Created
true

Unblock user

  • Method: POST
  • URL: /api/v4/users/:id/unblock
  • Watch out for: Returns 403 if the user was blocked by LDAP. LDAP-blocked users must be unblocked via LDAP, not the API.

Request example

POST /api/v4/users/99/unblock
PRIVATE-TOKEN: <admin_token>

Response example

HTTP 201 Created
true

Get current authenticated user

  • Method: GET
  • URL: /api/v4/user
  • Watch out for: Returns the profile of the token owner. Useful for token validation. Returns admin fields only if the token owner is an admin.

Request example

GET /api/v4/user
PRIVATE-TOKEN: <token>

Response example

{
  "id": 1,
  "username": "current_user",
  "name": "Current User",
  "email": "me@example.com",
  "state": "active",
  "two_factor_enabled": false
}

Rate limits, pagination, and events

  • Rate limits: GitLab.com enforces rate limits per user/IP. Unauthenticated requests are limited more strictly. Authenticated requests have higher limits. Self-managed instances have configurable limits.
  • Rate-limit headers: Yes
  • Retry-After header: Yes
  • Rate-limit notes: Rate limit headers include: RateLimit-Limit, RateLimit-Observed, RateLimit-Remaining, RateLimit-Reset, RateLimit-ResetTime, Retry-After (on 429). Some endpoints (e.g., user creation) may have stricter per-endpoint limits. GitLab.com also enforces 10 req/sec for the Users API specifically in some contexts.
  • Pagination method: offset
  • Default page size: 20
  • Max page size: 100
  • Pagination pointer: page and per_page. Response headers include: X-Total, X-Total-Pages, X-Page, X-Next-Page, X-Prev-Page, Link (RFC 5988).
Plan Limit Concurrent
GitLab.com (authenticated) 2,000 requests per minute (general REST API) 0
GitLab.com (unauthenticated) 500 requests per minute 0
Self-managed (default) Configurable; default 2,000 req/min for authenticated users 0
  • Webhooks available: Yes
  • Webhook notes: GitLab supports system hooks (self-managed, admin-level) and group/project webhooks. System hooks fire on user lifecycle events instance-wide. Group webhooks require Premium or Ultimate.
  • Alternative event strategy: Poll GET /api/v4/users with created_after or updated_after query params for change detection on GitLab.com where system hooks are unavailable.
  • Webhook events: user_create, user_destroy, user_rename, user_failed_login, member_create, member_update, member_destroy

SCIM API status

  • SCIM available: Yes

  • SCIM version: 2.0

  • Plan required: Premium or Ultimate (GitLab.com groups); requires Group SSO (SAML) to be configured first.

  • Endpoint: https://gitlab.com/api/scim/v2/groups/:group_path/Users

  • Supported operations: GET /Users (list provisioned users), GET /Users/:id (get single user), POST /Users (provision/create user), PATCH /Users/:id (update user attributes, including active status for deprovisioning), DELETE /Users/:id (remove user from group)

Limitations:

  • SCIM only manages group membership, not instance-level user accounts.
  • Requires SAML SSO to be enabled on the group before SCIM can be configured.
  • SCIM token is separate from PAT/OAuth tokens; generated in Group > Settings > SAML SSO.
  • Supported SCIM attributes are limited: userName, name.formatted, emails[primary], active, externalId.
  • DELETE via SCIM removes the user from the group but does not delete the GitLab account.
  • Self-managed GitLab uses a different SCIM endpoint path and requires GitLab 11.10+.
  • Only one SCIM token per group; rotating it invalidates the previous token immediately.
  • Google Workspace is not officially supported as a SCIM provider for GitLab.

Common scenarios

Provisioning a new employee requires two sequential API calls: POST /api/v4/users with reset_password:true to create the account (capture the returned id), then POST /api/v4/groups/:group_id/members with the user_id and numeric access_level to assign group membership.

On instances with email confirmation enabled, the user cannot authenticate until confirmed; pass skip_confirmation:true (admin, self-managed only) to bypass.

Offboarding via the REST API should follow a block-first pattern: POST /users/:id/block immediately revokes login while preserving all data, then enumerate and revoke active PATs via GET /users/:id/personal_access_tokens and DELETE /personal_access_tokens/:token_id. Hard deletion (DELETE /users/:id?hard_delete=true) is irreversible and removes all associated records - confirm data retention policy before use. LDAP-managed users cannot be blocked or unblocked via the API; the change must originate in LDAP.

For IdP-driven lifecycle management, the SCIM 2.0 endpoint (https://gitlab.com/api/scim/v2/groups/:group_path/Users) handles provisioning and deprovisioning against the identity graph at the group level. SCIM requires a dedicated token generated in Group > Settings > SAML SSO - not a PAT or OAuth token. Supported attributes are narrow: userName, name.formatted, emails[primary], active, externalId. A PATCH with active:false removes the user from the group but does not delete the GitLab.com account; the identity record persists and re-provisioning requires re-authentication via SAML.

Provision a new employee and add to a group

  1. POST /api/v4/users with email, username, name, reset_password:true using an admin PAT.
  2. Capture the returned user id from the response.
  3. POST /api/v4/groups/:group_id/members with user_id and access_level (e.g., 30 for Developer) to add the user to the appropriate group.
  4. Optionally POST /api/v4/projects/:project_id/members to add to specific projects.

Watch out for: If the instance requires email confirmation, the user cannot log in until they confirm. Use skip_confirmation:true (admin only) to bypass on self-managed instances.

Offboard a departing employee (soft delete)

  1. GET /api/v4/users?search= to retrieve the user id.
  2. POST /api/v4/users/:id/block to immediately revoke login access while preserving data.
  3. Optionally DELETE /api/v4/users/:id (without hard_delete) to remove the account and reassign contributions to the ghost user.
  4. Revoke any active personal access tokens via GET /api/v4/users/:id/personal_access_tokens and DELETE /api/v4/personal_access_tokens/:token_id.

Watch out for: Blocking is reversible; deletion (even soft) is not. Confirm data retention requirements before deleting. LDAP-synced users must be disabled in LDAP first.

Automated SCIM deprovisioning via IdP (Okta/Entra)

  1. Ensure SAML SSO is configured and active for the GitLab group (Premium or Ultimate required).
  2. Generate a SCIM token in Group > Settings > SAML SSO > Generate a SCIM token.
  3. Configure the IdP (Okta, Entra, OneLogin) SCIM connector with base URL https://gitlab.com/api/scim/v2/groups/:group_path and the SCIM token as Bearer auth.
  4. Map IdP attributes to SCIM fields: userName→username, emails[primary]→email, name.formatted→name, active→active.
  5. When a user is deactivated in the IdP, the IdP sends PATCH /Users/:id with active:false, removing the user from the GitLab group.

Watch out for: SCIM deprovisions group membership only; it does not delete the GitLab.com account. The user's account remains but loses group access. Re-provisioning requires the user to re-authenticate via SAML.

Why building this yourself is a trap

The most consequential API caveat is the hard_delete flag: DELETE /users/:id?hard_delete=true permanently destroys the user and all owned content with no recovery path. The default soft delete is safer - it blocks the account and reassigns contributions to a ghost user - but is still irreversible.

Any automated offboarding pipeline should gate on an explicit confirmation step before issuing either variant.

The identity graph built from GitLab's API is split across two auth surfaces: REST API tokens (PAT/OAuth) for instance-level user data, and a separate SCIM token for group-level provisioning.

These tokens are not interchangeable, and rotating the SCIM token immediately invalidates the previous one - a silent break for any IdP connector that has not been updated.

On GitLab.com, system hooks (which fire on user lifecycle events instance-wide) are unavailable; the only change-detection alternative is polling GET /api/v4/users with created_after or updated_after query parameters, which adds latency to any event-driven identity graph sync.

Automate GitLab 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 9, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

15Five logo

15Five

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

15Five uses a fixed role-based permission model with six predefined roles: Account Admin, HR Admin, Billing Admin, Group Admin, Manager, and Employee. No custom roles can be constructed. User management lives at Settings gear → People → Manage people p

1Password logo

1Password

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

1Password's admin console at my.1password.com covers the full user lifecycle — invitations, group assignments, vault access, suspension, and deletion — without any third-party tooling. Like every app that mixes role-based and resource-level permissions

8x8 logo

8x8

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

8x8 Admin Console supports full lifecycle user management — create, deactivate, and delete — across its X Series unified communications platform. Every app a user can access (8x8 Work desktop, mobile, web, Agent Workspace) is gated by license assignmen