Users
Updates the specified user's profile, license assignments, course access, and related attributes
Updates the specified user's profile, license assignments, course access, and related attributes.
https://example.thoughtindustries.com/incoming/v2/users/:idExample request
curl -X PUT "https://example.thoughtindustries.com/incoming/v2/users/:id" \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"clientId": "00000000-0000-0000-0000-000000000000",
"studentLicenseIds": ["00000000-0000-0000-0000-000000000000"],
"replaceLicenseAccess": true,
"panoramaEnrollmentOptions": {
"preserveEnrollments": false,
"preserveProgress": true
}
}'Parameters
| Name | Type | Required | Location | Description |
|---|---|---|---|---|
id | uuid | Yes | path | The id identifier. |
email | string | No | body | The user's email address. |
externalCustomerId | string | No | body | An external system identifier for the user, used to map the user to a record in an outside platform. |
studentLicenseSkus | string[] | No | body | SKUs of licenses to assign to the user in the student role. |
managerLicenseSkus | string[] | No | body | SKUs of licenses to assign to the user in the manager role. |
studentLicenseIds | uuid[] | No | body | IDs of licenses to assign to the user in the student role. |
managerLicenseIds | uuid[] | No | body | IDs of licenses to assign to the user in the manager role. |
role | string | No | body | The user's platform role (e.g., admin, learner). |
clientSku | string | No | body | SKU of the client (tenant) to associate with the user. |
clientSlug | string | No | body | URL slug of the client (tenant) to associate with the user. |
clientId | uuid | No | body | ID of the client (tenant) to associate with the user. |
replaceLicenseAccess | boolean | No | body | When true, replaces the user's existing license assignments with the ones provided; otherwise merges them. |
courseIds | uuid[] | No | body | IDs of courses to grant the user direct access to. |
courseSlugs | string[] | No | body | URL slugs of courses to grant the user direct access to. |
courseSkus | string[] | No | body | SKUs of courses to grant the user direct access to. |
bundleSlugs | string[] | No | body | URL slugs of bundles to grant the user access to. |
learningPathSlugs | string[] | No | body | URL slugs of learning paths to grant the user access to. |
learningPathSkus | string[] | No | body | SKUs of learning paths to grant the user access to. |
learningPathIds | uuid[] | No | body | IDs of learning paths to grant the user access to. |
firstName | string | No | body | The user's first name. |
lastName | string | No | body | The user's last name. |
address1 | string | No | body | The first line of the user's mailing address. |
address2 | string | No | body | The second line of the user's mailing address (e.g., suite or apartment number). |
city | string | No | body | The city component of the user's mailing address. |
state | string | No | body | The state or province component of the user's mailing address. |
zipCode | string | No | body | The postal code component of the user's mailing address. |
country | string | No | body | The country component of the user's mailing address. |
telephone | string | No | body | The user's phone number. |
ref1 | string | No | body | Custom reference field 1 for storing arbitrary external data. |
ref2 | string | No | body | Custom reference field 2 for storing arbitrary external data. |
ref3 | string | No | body | Custom reference field 3 for storing arbitrary external data. |
ref4 | string | No | body | Custom reference field 4 for storing arbitrary external data. |
ref5 | string | No | body | Custom reference field 5 for storing arbitrary external data. |
ref6 | string | No | body | Custom reference field 6 for storing arbitrary external data. |
ref7 | string | No | body | Custom reference field 7 for storing arbitrary external data. |
ref8 | string | No | body | Custom reference field 8 for storing arbitrary external data. |
ref9 | string | No | body | Custom reference field 9 for storing arbitrary external data. |
ref10 | string | No | body | Custom reference field 10 for storing arbitrary external data. |
language | string | No | body | The user's preferred language code (e.g., en, fr). |
tieredSubscription | boolean | No | body | When true, enables tiered subscription pricing for the user. |
replaceCourseAccess | boolean | No | body | When true, replaces the user's existing course access with the courses provided; otherwise merges them. |
replaceLearningPathAccess | boolean | No | body | When true, replaces the user's existing learning path access with the learning paths provided; otherwise merges them. |
replaceBundleAccess | boolean | No | body | When true, replaces the user's existing bundle access with the bundles provided; otherwise merges them. |
customFields | object | No | body | Key-value pairs of custom field data defined for the user's client. |
sfContactId | uuid | No | body | The Salesforce Contact ID linked to this user. |
sfAccountId | uuid | No | body | The Salesforce Account ID linked to this user. |
dualRole | boolean | No | body | When true, grants the user both learner and manager roles simultaneously. |
balance | number | No | body | The user's credit balance, in the platform's configured currency units. |
enforceAccessDays | boolean | No | body | When true, enforces license access-day limits on the user's content access. |
preferredCurrency | string | No | body | The user's preferred currency code for pricing display (e.g., USD, EUR). |
panoramaEnrollmentOptions | object | No | body | Controls how the learner's existing enrollments and progress are handled when this request moves them to a different Panorama (clientId) or replaces their license assignments (replaceLicenseAccess). Ignored when the request changes neither the Panorama nor the license assignments. When omitted, all enrollments and progress are preserved. See "Panorama enrollment options" below. |
panoramaEnrollmentOptions.preserveEnrollments | boolean | No | body | Defaults to true. When true, enrollments tied only to licenses the user is losing are kept as direct enrollments so the learner retains access; when false, those enrollments are removed. |
panoramaEnrollmentOptions.preserveProgress | boolean | No | body | Defaults to true. When true, the learner's course progress and completion history are retained; when false, progress for the removed enrollments is permanently reset. Must be true whenever preserveEnrollments is true. |
Panorama enrollment options
panoramaEnrollmentOptions only takes effect when this request actually changes the user's Panorama or license access — for example, when clientId moves the user to a different Panorama, or when license fields (such as studentLicenseIds or studentLicenseSkus) are combined with replaceLicenseAccess: true. On any other update the field is ignored.
Both options apply only to source-only enrollments: content the learner reaches through the licenses being removed and through no other license they keep or gain. Content available through both the old and the new licenses — overlap content — is never removed and never has its progress reset, whatever the two options are set to. Only its license association changes: the removed license IDs are dropped from the enrollment and the retained or newly added license that also covers it is stamped in their place. Courses the learner reaches through a learning path that survives the move are treated as overlap content for the same reason.
The object accepts two booleans, each defaulting to true. Three combinations are valid:
preserveEnrollments | preserveProgress | Effect |
|---|---|---|
true | true | Default (also applied when panoramaEnrollmentOptions is omitted). Enrollments from licenses the user is losing are converted to direct enrollments; the learner keeps access and all progress. This is the backward-compatible behavior. |
false | true | The learner loses access to content from the removed licenses, but their progress and completion history are retained. If they regain access later, they resume where they left off. Courses set to restart progress on re-enrollment are the exception — see the note below. |
false | false | The learner loses access to content from the removed licenses and their progress for that content is permanently reset. |
The fourth combination — preserveEnrollments: true with preserveProgress: false — is invalid, because progress cannot be reset while the enrollment is preserved. It is rejected without applying the update (see "Validation errors" below).
Always preserved:
preserveProgress: falseclears only in-course progress for the affected content — topic-level progress, the resume position, the completion percentage, and assignment submissions, quiz attempts, and SCORM attempts. Certificates are never revoked, hidden, or invalidated, and the course and learning path completion records that make up their transcript are left in place (milestone completions inside a reset learning path are cleared, because learning path progress is derived from them). Achievements and awards move with the learner to the new Panorama rather than being removed. No parameter on this endpoint resets certificates, transcripts, or achievements —panoramaEnrollmentOptionsaccepts only the two booleans above.
Note: Courses with Restart Progress After Re-Enrollment enabled are the one exception. That setting is off by default and is turned on per course, in the course's eCommerce settings under Access, typically for recertification. Whenever the learner loses access to such a course — any time
preserveEnrollmentsisfalse, including thefalse/truecombination — that course's saved progress, quiz attempts, and SCORM attempts are cleared and its completion entry is removed from the transcript. The certificate itself is retained, and if your company displays past completions on the transcript, the completion still appears there. This is long-standing platform behavior for any loss of access — the same reset happens when access expires or a learner is automatically unenrolled — not something specific topanoramaEnrollmentOptions.
Example response
{
"id": "00000000-0000-0000-0000-000000000000",
"learnerUserId": "00000000-0000-0000-0000-000000000000",
"managerUserId": "00000000-0000-0000-0000-000000000000",
"clientId": "00000000-0000-0000-0000-000000000000",
"createdAt": "2024-01-01T00:00:00.000Z",
"disabled": true,
"sfAccountId": "00000000-0000-0000-0000-000000000000",
"sfContactId": "00000000-0000-0000-0000-000000000000",
"roleKey": "string",
"address1": "string",
"address2": "string",
"city": "string",
"state": "string",
"zipCode": "string",
"telephone": "string",
"country": "string",
"stripeCustomerId": "00000000-0000-0000-0000-000000000000",
"externalCustomerId": "string",
"shippingName": "string",
"asset": "string",
"email": "string",
"firstName": "string",
"lastName": "string",
"lastActiveAt": "2024-01-01T00:00:00.000Z",
"ref1": "string",
"ref2": "string",
"ref3": "string",
"ref4": "string",
"ref5": "string",
"ref6": "string",
"ref7": "string",
"ref8": "string",
"ref9": "string",
"ref10": "string",
"language": "string",
"customFields": {},
"purchasedBundles": [
{
"bundleId": "00000000-0000-0000-0000-000000000000"
}
],
"allocatedLicenses": [
{
"licenseId": "00000000-0000-0000-0000-000000000000"
}
],
"allocatedLearningPaths": [
{
"learningPathId": "00000000-0000-0000-0000-000000000000",
"status": "string"
}
],
"purchasedCourses": [
{
"courseId": "00000000-0000-0000-0000-000000000000",
"status": "string"
}
],
"waitlistedCourses": [
{
"courseId": "00000000-0000-0000-0000-000000000000"
}
],
"balance": 0,
"preferredCurrency": "string"
}Validation errors
Supplying panoramaEnrollmentOptions with preserveEnrollments: true and preserveProgress: false is invalid — progress cannot be reset while the enrollment is preserved. The request is rejected and no changes are applied.
Unlike a successful response, which is unwrapped to the user object, this validation failure keeps the GraphQL envelope and is returned with HTTP status 200. Detect it by checking for an errors key rather than by status code:
{
"errors": [
{ "message": "Cannot reset progress while preserving enrollment" }
],
"data": {
"APIUpdateUser": null
}
}Note: Entries in
errorsmay carry additional GraphQL fields such aslocations,path, andextensions. Match onerrors[0].messagerather than on the exact object shape.
Response fields
| Field | Type | Description |
|---|---|---|
id | uuid | Unique identifier of the user. |
learnerUserId | uuid | Identifier of the associated learner user record, when the user has a dual role. |
managerUserId | uuid | Identifier of the associated manager user record, when the user has a dual role. |
clientId | uuid | Identifier of the client (tenant) the user belongs to. |
createdAt | ISO 8601 timestamp | Timestamp when the user was created. |
disabled | boolean | Whether the user account is disabled. |
sfAccountId | uuid | Salesforce Account ID associated with the user. |
sfContactId | uuid | Salesforce Contact ID associated with the user. |
roleKey | string | Platform role key assigned to the user. |
address1 | string | Primary street address line. |
address2 | string | Secondary street address line. |
city | string | City component of the address. |
state | string | State or province component of the address. |
zipCode | string | Postal code component of the address. |
telephone | string | User's phone number. |
country | string | Country component of the address. |
stripeCustomerId | uuid | Stripe customer identifier for the user. |
externalCustomerId | string | Identifier for the user in an external system. |
shippingName | string | Name used for shipping physical goods. |
asset | string | URL of the user's avatar asset. |
email | string | User's email address. |
firstName | string | User's first name. |
lastName | string | User's last name. |
lastActiveAt | ISO 8601 timestamp | Timestamp of the last recorded user activity. |
ref1 | string | Custom reference field 1 for storing arbitrary external data. |
ref2 | string | Custom reference field 2 for storing arbitrary external data. |
ref3 | string | Custom reference field 3 for storing arbitrary external data. |
ref4 | string | Custom reference field 4 for storing arbitrary external data. |
ref5 | string | Custom reference field 5 for storing arbitrary external data. |
ref6 | string | Custom reference field 6 for storing arbitrary external data. |
ref7 | string | Custom reference field 7 for storing arbitrary external data. |
ref8 | string | Custom reference field 8 for storing arbitrary external data. |
ref9 | string | Custom reference field 9 for storing arbitrary external data. |
ref10 | string | Custom reference field 10 for storing arbitrary external data. |
language | string | User's preferred language (BCP 47 tag). Null for non-learner roles. |
customFields | object | Key-value pairs for custom profile fields defined on the client. |
purchasedBundles | object[] | Bundles the user has purchased. |
purchasedBundles.bundleId | uuid | Identifier of the purchased bundle. |
allocatedLicenses | object[] | Licenses allocated to the user. |
allocatedLicenses.licenseId | uuid | Identifier of the allocated license. |
allocatedLearningPaths | object[] | Learning paths allocated to the user. |
allocatedLearningPaths.learningPathId | uuid | Identifier of the allocated learning path. |
allocatedLearningPaths.status | string | Status of the learning path allocation. |
purchasedCourses | object[] | Courses the user has purchased. |
purchasedCourses.courseId | uuid | Identifier of the purchased course. |
purchasedCourses.status | string | Enrollment status for the course. |
waitlistedCourses | object[] | Courses the user is waitlisted for. |
waitlistedCourses.courseId | uuid | Identifier of the waitlisted course. |
balance | number | User's current credit balance. |
preferredCurrency | string | User's preferred display currency (ISO 4217). |