Summary and recommendation
The Lessonly REST API is versioned at `/api/v1` and authenticates via HTTP Basic Auth - the company subdomain is the username and the API key is the password, Base64-encoded. This is not a Bearer token pattern; misconfiguring the auth header returns a 401 with no further detail.
The base URL must include the full versioned path segment (`https://api.lessonly.com/api/v1`); omitting `/api/v1` returns a 404.
Pagination is page-number-based with a hard page size of 50 and no cursor or link-header mechanism - iterate the `page` param until a response returns fewer than 50 records. Rate limits are not publicly documented; treat the limit as unknown and implement exponential backoff on 429 responses.
No webhook support is available; change detection requires scheduled polling of `GET /users` or `GET /users/{id}/assignments`.
The platform has been rebranded as Seismic Learning. Some documentation URLs under `lessonly.com` may redirect or be deprecated; cross-reference against `help.seismiclearning.com` when docs appear stale.
API quick reference
| Has user API | Yes |
| Auth method | API Key (HTTP Basic Auth: subdomain as username, API key as password, Base64-encoded) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise |
Authentication
Auth method: API Key (HTTP Basic Auth: subdomain as username, API key as password, Base64-encoded)
Setup steps
- Log in to Lessonly as an admin and navigate to Settings > Integrations > API.
- Copy your company subdomain and generate or retrieve your API key.
- Encode 'subdomain:api_key' in Base64.
- Pass the encoded value in the Authorization header: 'Authorization: Basic
'.
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | integer | Unique Lessonly user ID | system-assigned | immutable | Use in all user-specific endpoint paths. |
| name | string | Full display name | required | optional | |
| string | User email address (login identifier) | required | optional | Must be unique within the account. | |
| role | string | User role: 'learner', 'manager', or 'admin' | optional (defaults to learner) | optional | |
| ext_uid | string | External unique identifier for the user (e.g., HRIS ID) | optional | optional | Useful for cross-system correlation. |
| custom_user_field_data | array | Array of custom field key-value pairs | optional | optional | Custom fields must be pre-configured in Lessonly admin settings. |
| group_ids | array[integer] | IDs of groups the user belongs to | optional | optional | Replaces all existing group memberships on update. |
| archived_at | string (ISO 8601) | Timestamp when user was archived; null if active | n/a | read-only | Use the archive endpoint to archive users. |
| created_at | string (ISO 8601) | Account creation timestamp | system-assigned | immutable | |
| updated_at | string (ISO 8601) | Last modification timestamp | system-assigned | system-assigned |
Core endpoints
List Users
- Method: GET
- URL:
https://api.lessonly.com/api/v1/users - Watch out for: Returns up to 50 users per page. Iterate 'page' param until response returns fewer than 50 records.
Request example
GET /api/v1/users?page=1
Authorization: Basic <base64>
Response example
{
"type": "users",
"users": [
{"id": 101, "name": "Jane Doe", "email": "jane@example.com", "role": "learner"}
]
}
Show User
- Method: GET
- URL:
https://api.lessonly.com/api/v1/users/{user_id} - Watch out for: Use the integer Lessonly user ID, not email.
Request example
GET /api/v1/users/101
Authorization: Basic <base64>
Response example
{
"type": "user",
"id": 101,
"name": "Jane Doe",
"email": "jane@example.com",
"role": "learner",
"group_ids": [5, 12]
}
Create User
- Method: POST
- URL:
https://api.lessonly.com/api/v1/users - Watch out for: Email must be unique. Duplicate email returns an error, not a silent upsert.
Request example
POST /api/v1/users
Content-Type: application/json
{
"name": "John Smith",
"email": "john@example.com",
"role": "learner"
}
Response example
{
"type": "user",
"id": 202,
"name": "John Smith",
"email": "john@example.com",
"role": "learner"
}
Update User
- Method: PUT
- URL:
https://api.lessonly.com/api/v1/users/{user_id} - Watch out for: Sending 'group_ids' replaces all existing group memberships; omit the field to leave groups unchanged.
Request example
PUT /api/v1/users/202
Content-Type: application/json
{
"role": "manager",
"group_ids": [5]
}
Response example
{
"type": "user",
"id": 202,
"role": "manager",
"group_ids": [5]
}
Archive User
- Method: PUT
- URL:
https://api.lessonly.com/api/v1/users/{user_id}/archive - Watch out for: Archiving is not the same as deletion. Archived users retain historical completion data but cannot log in.
Request example
PUT /api/v1/users/202/archive
Authorization: Basic <base64>
Response example
{
"type": "user",
"id": 202,
"archived_at": "2024-06-01T12:00:00Z"
}
Restore User
- Method: PUT
- URL:
https://api.lessonly.com/api/v1/users/{user_id}/restore - Watch out for: Restoring a user reactivates their account but does not re-enroll them in previously assigned content.
Request example
PUT /api/v1/users/202/restore
Authorization: Basic <base64>
Response example
{
"type": "user",
"id": 202,
"archived_at": null
}
List Groups
- Method: GET
- URL:
https://api.lessonly.com/api/v1/groups - Watch out for: Group IDs are required when assigning users to groups via the user create/update endpoints.
Request example
GET /api/v1/groups
Authorization: Basic <base64>
Response example
{
"type": "groups",
"groups": [
{"id": 5, "name": "Sales Team"}
]
}
Show User Assignments
- Method: GET
- URL:
https://api.lessonly.com/api/v1/users/{user_id}/assignments - Watch out for: Returns all assignments regardless of status; filter client-side by 'status' field.
Request example
GET /api/v1/users/101/assignments
Authorization: Basic <base64>
Response example
{
"type": "assignments",
"assignments": [
{"id": 9, "assignable_type": "Lesson", "status": "completed"}
]
}
Rate limits, pagination, and events
Rate limits: Lessonly's public API documentation does not publish explicit rate limit numbers. Practical guidance from the developer docs suggests avoiding burst requests.
Rate-limit headers: Unknown
Retry-After header: Unknown
Rate-limit notes: No officially documented rate limit tiers or headers found. Treat as unknown; implement exponential backoff on 429 responses.
Pagination method: offset
Default page size: 50
Max page size: 50
Pagination pointer: page
Webhooks available: No
Webhook notes: No webhook functionality is documented in Lessonly's official API or help center documentation as of the research date.
Alternative event strategy: Poll the REST API (e.g., GET /users, GET /users/{id}/assignments) on a scheduled basis to detect changes.
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise
Endpoint: https://{subdomain}.lessonly.com/scim/v2
Supported operations: Create User (POST /Users), Update User (PUT /Users/{id}), Deactivate User (PATCH /Users/{id} with active=false), List Users (GET /Users), Get User (GET /Users/{id})
Limitations:
- SSO (SAML) must be configured before SCIM provisioning can be enabled.
- Azure AD / Microsoft Entra SCIM is not officially supported; only Okta and OneLogin are documented.
- Group push support varies by IdP configuration; verify in IdP integration settings.
- SCIM is gated to the Enterprise plan only.
Common scenarios
For provisioning into an identity graph, the recommended flow is: resolve the target group ID via GET /api/v1/groups, then POST /api/v1/users with name, email, role, and group_ids.
Store the returned integer id - all subsequent operations (update, archive, assignment lookup) require this Lessonly-native ID, not email. Email must be unique; a duplicate email returns an error, not a silent upsert, so pre-check with GET /api/v1/users and filter client-side before creating.
For offboarding, PUT /api/v1/users/{user_id}/archive is the correct action - there is no DELETE endpoint. Archiving sets archived_at and blocks login but preserves all completion history. When updating group membership, group_ids in a PUT /api/v1/users/{user_id} call is a full replacement: omit the field entirely if group membership should remain unchanged, or you will silently remove existing memberships.
For SCIM-based provisioning via Okta, SAML SSO must be fully configured in Lessonly before SCIM can be enabled - the dependency is hard. The SCIM base URL is https://{subdomain}.lessonly.com/scim/v2. Azure AD / Microsoft Entra SCIM is not officially supported; provisioning failures in that configuration are outside Lessonly support scope.
Provision a new employee and assign to a group
- GET /api/v1/groups to retrieve the target group ID.
- POST /api/v1/users with 'name', 'email', 'role', and 'group_ids' containing the retrieved group ID.
- Store the returned 'id' in your system for future updates.
Watch out for: If the email already exists, the API returns an error. Check for existing users with GET /api/v1/users and filter by email before creating.
Offboard a departing employee
- Identify the user's Lessonly ID via GET /api/v1/users (filter by email client-side).
- PUT /api/v1/users/{user_id}/archive to deactivate the account.
- Optionally verify the 'archived_at' field is populated in the response.
Watch out for: Archiving preserves completion history. There is no hard-delete endpoint; archived users remain in the system.
Automated SCIM provisioning via Okta
- Confirm the Lessonly account is on the Enterprise plan.
- Configure SAML SSO in Lessonly Settings > Integrations > SSO before enabling SCIM.
- In Okta, add the Lessonly application from the OIN and enable SCIM provisioning.
- Enter the SCIM base URL (https://{subdomain}.lessonly.com/scim/v2) and the SCIM API token from Lessonly admin settings.
- Map Okta profile attributes to Lessonly SCIM attributes and enable Create, Update, and Deactivate operations.
- Test with a single user assignment before enabling group push.
Watch out for: Azure AD SCIM is not officially supported. Attempting to configure it may result in provisioning failures not covered by Lessonly support.
Why building this yourself is a trap
The most common integration trap is treating group_ids as an additive patch - it is not. A PUT to update any user field while including group_ids will replace all existing group memberships with exactly the array provided.
Pipelines that update user attributes without explicitly carrying forward current group state will silently strip group assignments, breaking content delivery without any error signal.
The second trap is the SCIM/manual deactivation conflict: archiving a user via the API or admin UI while the IdP still marks them active will result in re-provisioning on the next SCIM sync cycle.
Any offboarding automation must disable the user in the IdP first, then allow SCIM to propagate the deactivation - not the reverse.
Custom user fields are a third failure point: fields must be created in the Lessonly admin UI before they can be written via custom_user_field_data in the API; attempting to set an undeclared field does not return a validation error in all documented cases.
Automate Lessonly 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.