Summary and recommendation
Docebo's REST API uses OAuth 2.0 client_credentials grant. Obtain a Bearer token via POST to `https://{domain}.docebosaas.com/oauth2/token`; tokens expire in 3600 seconds - cache and reuse them, do not request a new token per call.
The user management base path is `/api/v1/manage/v1/user`; note that other resource areas (e.g., `/api/v1/learn/v1/`) use different versioned path prefixes, which is a common source of 404 errors.
SCIM 2.0 is available at `https://{domain}.docebosaas.com/scim/v2` but requires the Enterprise plan. Elevate-tier customers must use the REST API for all provisioning.
For teams building identity graph pipelines - correlating Docebo user records with IdP identities, HRIS profiles, and downstream SaaS accounts - the REST API's `user_id`, `username`, and `email` fields serve as the primary join keys across systems.
Pagination is offset-based with a hard cap of 200 records per page (`page` and `page_size` params). Rate limits are enforced per API client but exact thresholds are not publicly documented; implement exponential backoff on HTTP 429 responses.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 (client_credentials grant) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise |
Authentication
Auth method: OAuth 2.0 (client_credentials grant)
Setup steps
- Log in to Docebo as a Superadmin and navigate to Admin Menu > API & SSO > API Credentials.
- Create a new API client, recording the Client ID and Client Secret.
- POST to https://{your-domain}.docebosaas.com/oauth2/token with grant_type=client_credentials, client_id, and client_secret to obtain a Bearer access token.
- Include the token in all API requests as Authorization: Bearer {access_token}.
- Tokens expire; re-request using the same client credentials flow when a 401 is returned.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| api:manage | Full read/write access to all API resources including user management. | Creating, updating, deleting users and group/branch assignments. |
| api:read | Read-only access to API resources. | Listing and retrieving user records. |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| user_id | integer | Unique internal user identifier. | system-assigned | read-only | Used as path parameter for update/delete. |
| username | string | Unique login username. | required | optional | Must be unique across the platform. |
| string | User email address. | required | optional | Used for notifications and login if configured. | |
| password | string | User password (hashed on storage). | required | optional | Not returned in GET responses. |
| first_name | string | User's first name. | required | optional | |
| last_name | string | User's last name. | required | optional | |
| status | string | Account status: active, inactive, deleted. | optional (defaults to active) | optional | Setting to inactive disables login. |
| role | string | Platform role: user, poweruser, godadmin. | optional | optional | |
| language | string | ISO 639-1 language code for UI locale. | optional | optional | |
| timezone | string | IANA timezone string. | optional | optional | |
| date_of_birth | string (YYYY-MM-DD) | User date of birth. | optional | optional | |
| level | integer | User level/grade within the platform. | optional | optional | |
| avatar | string (URL) | URL to user avatar image. | optional | optional | Read-only in most contexts; set via separate upload endpoint. |
| branch_id | integer | ID of the organizational branch the user belongs to. | optional | optional | Assign via branch enrollment endpoint, not directly on user object in all API versions. |
| additional_fields | object | Key-value map of custom user additional fields configured in the platform. | optional | optional | Field keys are platform-specific slugs. |
| expiration_date | string (YYYY-MM-DD) | Date after which the user account expires. | optional | optional | |
| send_notification_email | boolean | Whether to send a welcome email on creation. | optional | n/a | |
| manager_id | integer | User ID of the direct manager. | optional | optional | Cannot be provisioned via SCIM; must use REST API or manual assignment. |
Core endpoints
List Users
- Method: GET
- URL:
https://{domain}.docebosaas.com/api/v1/manage/v1/user - Watch out for: Max page_size is 200. Use page parameter to iterate. search_text filters by username/email/name.
Request example
GET /api/v1/manage/v1/user?page=1&page_size=200&search_text=john
Authorization: Bearer {token}
Response example
{
"data": {
"items": [{"user_id":123,"username":"jdoe","email":"j@example.com"}],
"count": 1,
"total_count": 1
}
}
Get User by ID
- Method: GET
- URL:
https://{domain}.docebosaas.com/api/v1/manage/v1/user/{user_id} - Watch out for: Returns 404 if user_id does not exist or is deleted.
Request example
GET /api/v1/manage/v1/user/123
Authorization: Bearer {token}
Response example
{
"data": {
"user_id": 123,
"username": "jdoe",
"email": "j@example.com",
"status": "active"
}
}
Create User
- Method: POST
- URL:
https://{domain}.docebosaas.com/api/v1/manage/v1/user - Watch out for: username and email must be unique. Password complexity rules are enforced per platform settings.
Request example
POST /api/v1/manage/v1/user
Content-Type: application/json
{
"username":"jdoe","email":"j@example.com",
"password":"Secure123!","first_name":"John","last_name":"Doe"
}
Response example
{
"data": {
"user_id": 456,
"username": "jdoe",
"email": "j@example.com"
}
}
Update User
- Method: PUT
- URL:
https://{domain}.docebosaas.com/api/v1/manage/v1/user/{user_id} - Watch out for: Uses PUT (full-style), but only fields provided are updated in practice. Verify behavior against your platform version.
Request example
PUT /api/v1/manage/v1/user/456
Content-Type: application/json
{
"first_name":"Jonathan",
"status":"inactive"
}
Response example
{
"data": {
"user_id": 456,
"first_name": "Jonathan",
"status": "inactive"
}
}
Delete User
- Method: DELETE
- URL:
https://{domain}.docebosaas.com/api/v1/manage/v1/user/{user_id} - Watch out for: Deletion is soft by default (status set to deleted). Hard deletion may require additional platform configuration.
Request example
DELETE /api/v1/manage/v1/user/456
Authorization: Bearer {token}
Response example
{
"data": {
"success": true
}
}
Assign User to Branch
- Method: POST
- URL:
https://{domain}.docebosaas.com/api/v1/manage/v1/orgchart/branch/{branch_id}/user - Watch out for: Branch assignment is separate from user creation. A user must exist before being assigned to a branch.
Request example
POST /api/v1/manage/v1/orgchart/branch/10/user
Content-Type: application/json
{"user_id": 456}
Response example
{
"data": {
"success": true
}
}
Enroll User in Group
- Method: POST
- URL:
https://{domain}.docebosaas.com/api/v1/manage/v1/group/{group_id}/user - Watch out for: Groups in Docebo are dynamic or static; static groups accept direct enrollment via API. Dynamic groups are rule-based and cannot be directly enrolled.
Request example
POST /api/v1/manage/v1/group/5/user
Content-Type: application/json
{"user_id": 456}
Response example
{
"data": {
"success": true
}
}
Get OAuth2 Token
- Method: POST
- URL:
https://{domain}.docebosaas.com/oauth2/token - Watch out for: Tokens expire in 3600 seconds. Cache and reuse tokens; do not request a new token per API call.
Request example
POST /oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=abc&client_secret=xyz
Response example
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600
}
Rate limits, pagination, and events
- Rate limits: Docebo enforces rate limits per API client. Official documentation does not publicly specify exact request-per-minute figures; limits are enforced at the platform level and vary by contract.
- Rate-limit headers: Yes
- Retry-After header: No
- Rate-limit notes: HTTP 429 is returned when limits are exceeded. Docebo recommends exponential backoff. Specific header names for rate limit state are not publicly documented.
- Pagination method: offset
- Default page size: 200
- Max page size: 200
- Pagination pointer: page / page_size
| Plan | Limit | Concurrent |
|---|---|---|
| Elevate / Enterprise | Not publicly documented; contact Docebo support for contract-specific limits. | 0 |
- Webhooks available: Yes
- Webhook notes: Docebo supports webhooks (called 'Notifications' or 'Event Notifications') that fire on platform events including user lifecycle events. Configured in Admin Menu > Notification Center.
- Alternative event strategy: Polling the List Users endpoint with updated_from filter as an alternative to webhooks for sync scenarios.
- Webhook events: user.created, user.updated, user.deleted, user.enrolled_in_course, user.completed_course, user.login
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise
Endpoint: https://{domain}.docebosaas.com/scim/v2
Supported operations: GET /Users, GET /Users/{id}, POST /Users, PUT /Users/{id}, PATCH /Users/{id}, DELETE /Users/{id}, GET /Groups, POST /Groups, PATCH /Groups/{id}, DELETE /Groups/{id}
Limitations:
- Requires Enterprise plan; not available on Elevate tier.
- manager attribute (direct manager field) cannot be provisioned via SCIM.
- SCIM Groups map to Docebo Groups, not Branches; branch assignment requires REST API.
- Custom additional_fields may have limited SCIM attribute mapping support.
- Tested integrations documented for Okta, Microsoft Entra ID (Azure AD), and OneLogin.
Common scenarios
Three integration patterns cover the majority of Docebo API use cases.
IdP provisioning via SCIM (Enterprise only): Configure the SCIM endpoint in Docebo, map IdP attributes (userName, emails, name.givenName, name.familyName) in Okta or Entra, then assign users to the Docebo app to trigger SCIM POST /Users. Branch assignment and manager_id are not supported by SCIM and require a follow-up REST API call (POST /orgchart/branch/{branch_id}/user and PUT /user/{user_id} respectively) - this two-step pattern is unavoidable regardless of IdP.
Bulk deactivation from HRIS offboarding: Paginate GET /manage/v1/user?status=active across all pages (max 200/page), cross-reference against the HR system, then issue individual PUT /user/{user_id} with {"status": "inactive"} per departed user. There is no batch update endpoint; every record requires its own request. Confirm contract terms on soft-deleted users - status=deleted records may still count against license limits.
HRIS profile sync: Query the HRIS for records changed since the last sync timestamp, resolve each to a Docebo user_id via GET /manage/v1/user?search_text={email}, then PUT /user/{user_id} with changed fields. If branch_id changed, issue a separate POST /orgchart/branch/{new_branch_id}/user. Log all responses and retry 429s with backoff; there is no bulk update path.
Provision a new employee from an IdP (Okta/Entra)
- Configure SCIM 2.0 in Docebo Admin (Enterprise plan required): Admin Menu > API & SSO > SCIM.
- In the IdP (Okta or Entra), add Docebo as a SCIM application using the endpoint https://{domain}.docebosaas.com/scim/v2 and a Bearer token generated from Docebo.
- Map IdP attributes to SCIM attributes: userName→username, emails→email, name.givenName→first_name, name.familyName→last_name.
- Assign the user to the Docebo application in the IdP; SCIM POST /Users creates the user in Docebo.
- For branch assignment (not supported via SCIM), use the Docebo REST API POST /orgchart/branch/{branch_id}/user after provisioning.
- For manager assignment, use REST API PUT /user/{user_id} with manager_id field post-provisioning.
Watch out for: manager_id and branch assignment cannot be set via SCIM; a supplemental REST API call or manual step is required.
Bulk deactivate users who have left the organization
- Obtain OAuth 2.0 Bearer token via POST /oauth2/token with client_credentials.
- GET /api/v1/manage/v1/user?status=active&page=1&page_size=200 to retrieve active users; paginate through all pages.
- Cross-reference with HR system to identify departed users.
- For each departed user, PUT /api/v1/manage/v1/user/{user_id} with body {"status": "inactive"} to disable login.
- Optionally DELETE /api/v1/manage/v1/user/{user_id} for soft deletion if required by data retention policy.
Watch out for: Soft-deleted users may still count against license limits; confirm contract terms. Batch operations are not natively supported; loop requests individually.
Sync user profile updates from HRIS to Docebo
- Obtain OAuth 2.0 Bearer token.
- Query HRIS for changed employee records since last sync timestamp.
- For each changed record, GET /api/v1/manage/v1/user?search_text={email} to find the Docebo user_id.
- PUT /api/v1/manage/v1/user/{user_id} with updated fields (first_name, last_name, additional_fields, etc.).
- If department/branch changed, POST /api/v1/manage/v1/orgchart/branch/{new_branch_id}/user to reassign branch.
- Log API responses and retry on 429 with exponential backoff.
Watch out for: There is no bulk update endpoint; each user requires an individual PUT request. Rate limits apply; implement backoff and batching logic.
Why building this yourself is a trap
Several API behaviors are non-obvious and will cause silent failures or unexpected state if not handled explicitly.
SCIM does not provision manager_id or branch assignment. Both require supplemental REST API calls after SCIM provisioning completes. Pipelines that rely on SCIM alone will produce users with no branch and no manager - fields that affect reporting scope and Power User visibility.
Dynamic groups reject direct API enrollment. POST /group/{group_id}/user only works for static groups. Attempting to enroll a user in a dynamic (rule-based) group via API will fail or be silently ignored; membership is controlled by the group's rule engine, not direct assignment.
PUT semantics are inconsistent. The Update User endpoint is documented as PUT but behaves as a partial update in practice - only supplied fields are modified. Verify this behavior against the specific platform version before assuming full-replacement semantics in write logic.
Soft deletes persist in the system. DELETE /user/{user_id} sets status=deleted by default; the record remains queryable and may count against license limits depending on contract terms. Hard deletion requires additional platform configuration.
The identity graph implication: because manager_id and branch_id cannot be set atomically at user creation - branch requires a separate endpoint, manager requires REST even when SCIM is active - any identity graph that models Docebo org structure must treat these as asynchronous attributes, written in a second pass and reconciled on subsequent sync cycles, not assumed present immediately after provisioning.
Automate Docebo 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.