Users
The Users API returns and manages the learners, authors, managers, and admins in
your tenant. Reading (GET), creating (POST), updating (PATCH), and
deactivating (DELETE, a soft delete) users are all available. Reads need
the read scope, creates and updates the write scope, and deactivation the
admin scope. You can still provision users in bulk via
SCIM 2.0 or CSV import.
The User Object
{
"id": "a3f2c1d0-4e5b-6789-abcd-ef0123456789",
"email": "jane.doe@acme.com",
"first_name": "Jane",
"last_name": "Doe",
"role": "learner",
"is_active": true,
"external_id": "EMP-00412",
"created_at": "2026-01-15T09:00:00Z"
}| Field | Type | Notes |
|---|---|---|
id | uuid | User UUID |
email | string | Unique within the tenant |
first_name | string | First name |
last_name | string | Last name |
role | string | learner, author, manager, or admin |
is_active | boolean | false = deactivated (cannot sign in) |
external_id | string | null | Your system’s identifier (e.g. HRIS employee id), if set |
created_at | ISO 8601 | When the user was created |
Soft-deleted users are never returned.
Endpoints
List Users
GET /api/v1/usersReturns a paginated list of users in the tenant, most recently created first.
Authentication required: API key with the read scope
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | No | Page number. Default: 1 |
per_page | integer | No | Results per page. Default: 50, max: 200 |
role | string | No | Filter by role: learner, author, manager, admin |
Example Request
curl "https://{your-subdomain}.embaylms.com/api/v1/users?role=learner&per_page=25" \
-H "Authorization: Bearer ems_live_0000000000000000000000000000"Example Response
{
"data": [
{
"id": "a3f2c1d0-4e5b-6789-abcd-ef0123456789",
"email": "jane.doe@acme.com",
"first_name": "Jane",
"last_name": "Doe",
"role": "learner",
"is_active": true,
"external_id": "EMP-00412",
"created_at": "2026-01-15T09:00:00Z"
}
],
"meta": {
"page": 1,
"per_page": 25,
"total": 342
},
"error": null
}Common Errors
Errors use the standard envelope with a numeric error.code matching the HTTP
status — see Errors.
| HTTP Status | Description |
|---|---|
401 | Missing or invalid API key |
403 | Key lacks the read scope, or the API is not included in your plan |
429 | Rate limit or monthly usage cap exceeded — see Rate Limits |
Mostly available (
LMS-573).POST /users,GET /users/{id},PATCH /users/{id},DELETE /users/{id}(deactivate), and the additionalGET /userslist filters (group_id,is_active,created_after,created_before,sort_by,sort_order) are live. Writes require thewritescope; deactivate requiresadmin. Still planned — not yet available (return404today): the sub-resource endpointsGET /users/{id}/enrollments(useGET /api/v1/enrollments?user_id={id}) andGET /users/{id}/transcript.
Additional List Filters
The following GET /api/v1/users query parameters are available:
| Parameter | Type | Description |
|---|---|---|
is_active | boolean | Filter by active status: true or false |
group_id | UUID | Filter users belonging to a specific group |
created_after | ISO 8601 | Filter users created after this timestamp |
created_before | ISO 8601 | Filter users created before this timestamp |
sort_by | string | Field to sort by: created_at, last_name, email. Default: created_at |
sort_order | string | asc or desc. Default: desc |
The write API also extends the User object with group_ids, custom_fields,
last_login_at, and updated_at fields, as shown in the examples below.
Create User
POST /api/v1/usersCreates a new user in the tenant. API-created users have no password set — they authenticate via SSO, or set a password through the password-reset (invite-link) flow. No email is sent on creation.
Authentication required: write scope
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | User’s email address. Must be unique within the tenant |
first_name | string | Yes | First name |
last_name | string | Yes | Last name |
role | string | Yes | learner, author, manager, or admin. Assigning manager or admin requires the admin scope |
is_active | boolean | No | Whether the user can sign in. Default: true |
external_id | string | No | Your system’s identifier (e.g. HRIS employee id) |
group_ids | UUID[] | No | Array of group UUIDs to assign on creation (max 100) |
custom_fields | object | No | Key-value pairs matching your tenant’s configured custom fields |
locale | string | No | User’s preferred language: en or fr-CA. Default: en |
Example Request
curl -X POST https://{your-subdomain}.embaylms.com/api/v1/users \
-H "Authorization: Bearer ems_live_0000000000000000000000000000" \
-H "Content-Type: application/json" \
-d '{
"email": "john.smith@acme.com",
"first_name": "John",
"last_name": "Smith",
"role": "learner",
"external_id": "EMP-00413",
"group_ids": ["b4c3d2e1-5f6a-7890-bcde-f01234567890"],
"custom_fields": { "employee_id": "EMP-00413" }
}'Example Response
{
"data": {
"id": "c5d4e3f2-6a7b-8901-cdef-012345678901",
"email": "john.smith@acme.com",
"first_name": "John",
"last_name": "Smith",
"role": "learner",
"is_active": true,
"external_id": "EMP-00413",
"group_ids": ["b4c3d2e1-5f6a-7890-bcde-f01234567890"],
"custom_fields": { "employee_id": "EMP-00413" },
"created_at": "2026-06-05T10:00:00Z",
"updated_at": "2026-06-05T10:00:00Z"
},
"meta": null,
"error": null
}HTTP Status: 201 Created
Common Errors
| HTTP Status | Description |
|---|---|
400 | Missing required field or invalid value |
409 | A user with this email already exists in the tenant |
Get User
GET /api/v1/users/{id}Returns a single user by their UUID.
Authentication required: read scope
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | User UUID |
Example Request
curl "https://{your-subdomain}.embaylms.com/api/v1/users/a3f2c1d0-4e5b-6789-abcd-ef0123456789" \
-H "Authorization: Bearer ems_live_0000000000000000000000000000"Example Response
{
"data": {
"id": "a3f2c1d0-4e5b-6789-abcd-ef0123456789",
"email": "jane.doe@acme.com",
"first_name": "Jane",
"last_name": "Doe",
"role": "learner",
"is_active": true,
"group_ids": ["b4c3d2e1-5f6a-7890-bcde-f01234567890"],
"custom_fields": { "employee_id": "EMP-00412" },
"last_login_at": "2026-06-04T14:22:11Z",
"created_at": "2026-01-15T09:00:00Z",
"updated_at": "2026-06-04T14:22:11Z"
},
"meta": null,
"error": null
}Common Errors
| HTTP Status | Description |
|---|---|
404 | No user with this ID in the tenant |
Update User
PATCH /api/v1/users/{id}Updates one or more fields on an existing user. Only fields included in the request body are changed.
Authentication required: write scope
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | User UUID |
Request Body
All fields are optional. Include only fields you want to change.
| Field | Type | Description |
|---|---|---|
first_name | string | First name |
last_name | string | Last name |
role | string | learner, author, manager, or admin. Assigning manager or admin requires the admin scope |
is_active | boolean | true (can sign in) or false (deactivated) |
external_id | string | null | Your system’s identifier; null to clear |
custom_fields | object | null | Replaces the user’s custom fields object; null to clear |
locale | string | en or fr-CA |
Group membership is not managed here — use the
Groups API (POST / DELETE /api/v1/groups/{id}/members) to add or
remove a user from groups.
Example Request
curl -X PATCH "https://{your-subdomain}.embaylms.com/api/v1/users/a3f2c1d0-4e5b-6789-abcd-ef0123456789" \
-H "Authorization: Bearer ems_live_0000000000000000000000000000" \
-H "Content-Type: application/json" \
-d '{ "role": "author", "custom_fields": { "department": "L&D" } }'Example Response
{
"data": {
"id": "a3f2c1d0-4e5b-6789-abcd-ef0123456789",
"email": "jane.doe@acme.com",
"first_name": "Jane",
"last_name": "Doe",
"role": "author",
"is_active": true,
"external_id": "EMP-00412",
"custom_fields": { "employee_id": "EMP-00412", "department": "L&D" },
"last_login_at": "2026-06-04T14:22:11Z",
"created_at": "2026-01-15T09:00:00Z",
"updated_at": "2026-06-05T10:05:00Z"
},
"meta": null,
"error": null
}Deactivate User
DELETE /api/v1/users/{id}Deactivates a user. This is a soft delete — the user’s record, learning history, completions, and certificates are preserved. The user cannot log in after deactivation.
Authentication required: admin scope
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | User UUID |
Example Request
curl -X DELETE "https://{your-subdomain}.embaylms.com/api/v1/users/a3f2c1d0-4e5b-6789-abcd-ef0123456789" \
-H "Authorization: Bearer ems_live_0000000000000000000000000000"Example Response
HTTP Status: 204 No Content
Get User Enrollments
GET /api/v1/users/{id}/enrollments⚠️ Planned — not yet available (returns
404today).
Returns all enrollments for a specific user.
Live alternative:
GET /api/v1/enrollments?user_id={id}— see Enrollments.
Authentication required: read scope
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | User UUID |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | No | Filter: active, completed, dropped, expired, waitlisted, pending_approval |
page | integer | No | Default: 1 |
per_page | integer | No | Default: 50, max: 200 |
Example Request
curl "https://{your-subdomain}.embaylms.com/api/v1/users/a3f2c1d0-4e5b-6789-abcd-ef0123456789/enrollments?status=active" \
-H "Authorization: Bearer ems_live_0000000000000000000000000000"Example Response
{
"data": [
{
"id": "d6e5f4a3-7b8c-9012-defa-123456789012",
"course_id": "e7f6a5b4-8c9d-0123-efab-234567890123",
"course_title": "Workplace Safety Fundamentals",
"status": "active",
"progress_percent": 65,
"enrolled_at": "2026-05-01T08:00:00Z",
"due_date": "2026-07-01T00:00:00Z",
"completed_at": null,
"score": null
}
],
"meta": { "page": 1, "per_page": 50, "total": 8 },
"error": null
}Get User Transcript
GET /api/v1/users/{id}/transcript⚠️ Planned — not yet available (returns
404today).
Returns the full learning transcript for a user — all completed and in-progress learning activities, including certificates.
Authentication required: read scope
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | User UUID |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
completed_after | ISO 8601 | No | Filter completions after this date |
completed_before | ISO 8601 | No | Filter completions before this date |
format | string | No | json (default) or csv (triggers a download) |
Example Request
curl "https://{your-subdomain}.embaylms.com/api/v1/users/a3f2c1d0-4e5b-6789-abcd-ef0123456789/transcript" \
-H "Authorization: Bearer ems_live_0000000000000000000000000000"Example Response
{
"data": {
"user_id": "a3f2c1d0-4e5b-6789-abcd-ef0123456789",
"generated_at": "2026-06-05T10:00:00Z",
"completions": [
{
"enrollment_id": "d6e5f4a3-7b8c-9012-defa-123456789012",
"course_id": "e7f6a5b4-8c9d-0123-efab-234567890123",
"course_title": "WHMIS 2015",
"completed_at": "2026-03-15T11:30:00Z",
"score": 92,
"pass_fail": "pass",
"certificate_id": "f8a7b6c5-9d0e-1234-fabc-345678901234",
"certificate_expires_at": "2027-03-15T00:00:00Z"
}
]
},
"meta": null,
"error": null
}Bulk Create / Update Users
⚠️ Planned — not yet available (returns
404today; part of the LMS-539 bulk API). For bulk operations that are live today, usePOST /api/v1/enrollments/bulk(see Enrollments) or provision users via SCIM 2.0.
POST /api/v1/users/bulkCreates or updates up to 500 users in a single request. Matched on email — if a user with the given email exists, they are updated; otherwise, a new user is created.
Authentication required: write scope
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
users | User[] | Yes | Array of user objects (max 500) |
send_invites | boolean | No | Send invitation emails to newly created users. Default: false |
on_conflict | string | No | update (default) or skip — what to do when a user already exists |
Each user object in the array accepts the same fields as Create User.
Example Request
curl -X POST https://{your-subdomain}.embaylms.com/api/v1/users/bulk \
-H "Authorization: Bearer ems_live_0000000000000000000000000000" \
-H "Content-Type: application/json" \
-d '{
"users": [
{
"email": "alice.martin@acme.com",
"first_name": "Alice",
"last_name": "Martin",
"role": "learner",
"custom_fields": { "employee_id": "EMP-00414" }
},
{
"email": "bob.chen@acme.com",
"first_name": "Bob",
"last_name": "Chen",
"role": "learner",
"custom_fields": { "employee_id": "EMP-00415" }
}
],
"send_invites": false
}'Example Response
{
"data": {
"created": 1,
"updated": 1,
"skipped": 0,
"errors": [],
"users": [
{
"email": "alice.martin@acme.com",
"id": "09a8b7c6-1d2e-3456-abcd-567890123456",
"result": "created"
},
{
"email": "bob.chen@acme.com",
"id": "1a2b3c4d-5e6f-7890-bcde-678901234567",
"result": "updated"
}
]
},
"meta": null,
"error": null
}If individual rows fail validation, they are reported in errors and the rest of the batch is processed:
{
"errors": [
{
"row": 3,
"email": "invalid-not-an-email",
"message": "email: Invalid email address"
}
]
}HTTP Status: 207 Multi-Status when there are partial errors; 201 when all succeed.
Common Errors
| HTTP Status | Description |
|---|---|
400 | users array is empty or exceeds 500 |
429 | Bulk endpoint limit exceeded |