Skip to the reference

Partner API · v1.6.1

Creatium Assess API

Catalog, learner results, enrollment and assignments for learning platforms that list Creatium Assess assessments.

Production
https://app.assess.creatium.com/api/v1
Sandbox / UAT
https://staging.app.assess.creatium.com/api/v1

Overview

The Creatium Assess Partner API lets a learning platform, or an organization's own systems, import its assessment catalog, synchronize learner attempts and results, and optionally enroll learners or give them assignments ahead of launch.

Identity. Learners are identified by enterprise email address, compared case-insensitively and returned in lower case. It is the same address the organization's SAML 2.0 identity provider sends as NameID. Every person also has a personId (UUID) that stays the same when their email changes (POST /people/{personId}:changeEmail), and can carry the employer's own externalId. An assessmentId is a UUID. It is stable for the life of the assessment, identical in every endpoint, and never reused.

Tenancy. Every client belongs to one Creatium organization. Every request sees only that organization's assessments, learners and results.

Transport. HTTPS only, TLS 1.2 or later: TLS 1.0 and 1.1 are refused. Plain http:// answers 308 to the same https:// address, but call https directly: a token sent over http has already crossed the network in the clear. Responses carry Strict-Transport-Security.

Conventions. JSON over HTTPS. Timestamps are returned in ISO 8601 UTC with a Z suffix; timestamps sent with another offset are accepted and converted. Field names are camelCase. List endpoints use cursor pagination (see Pagination); a cursor only works with the filters it was issued for (other filters give 400). An ID in the path that is not a UUID gives 400; a well-formed one that matches nothing gives 404. An unknown path gives 404 and a method a path doesn't take gives 405 with Allow, both as problem details. Errors use RFC 9457 problem details (see Problem); each error code is explained at https://app.assess.creatium.com/docs#error-<code>. Unknown fields may be added to responses at any time within a version, so clients should ignore fields they do not recognize.

Response shape. Reads wrap their payload in data (lists add pagination). Six writes return the object itself: POST /enrollments, POST /assignments, GET and PATCH /assignments/{assignmentId}, POST /assignments/{assignmentId}/members and PUT /people/{learnerEmail}. v2 will wrap every response.

Personal data in paths. Paths that name a person (/enrollments/{assessmentId}/{learnerEmail}, /assignments/{assignmentId}/members/{learnerEmail}, /people/{learnerEmail}) take either the email address or the personId. Prefer the personId: proxies and access logs may record paths. v2 will take only the personId.

Data retention. Results are kept while the organization is a customer and deleted 90 days after it leaves. Proctoring readings are kept 90 days. A REMOVED record stays in /results for 30 days. Delivered webhook events are kept 30 days. POST /people/{personId}:erase removes a person on request (GDPR, CCPA) at any time. Data is stored in the United States; other regions on request.

Launch options. Besides launchUrl (SP-initiated SSO), every assessment can be delivered from an LMS as a SCORM 1.2 package (Assessment Designer → Share → SCORM) or embedded in an iframe at /embed/{assessmentId} from domains the organization allows (Admin → Embed). Results from every route appear in /results.

Concurrency and retries. GET /assignments/{assignmentId} returns an ETag; send it back as If-Match on PATCH and a change made by someone else in between gives 412. Every POST, PATCH and PUT takes an Idempotency-Key.

Managers and privacy. People can carry managerEmail, department and location (PUT /people, POST /people:batch). Person-level reads and analytics take team, managerEmail and includeIndirect to narrow themselves. A client can get a token *for a manager* (token exchange, see /oauth/token): it reads only, and only that manager's reports, direct and indirect. What such a token may see, how small a group analytics will show, and whether proctoring and answer keys need their own scopes is the organization's policy (GET /organization/policy, set by its admins).

Events. Every change a webhook announces is also kept 30 days at GET /events, so a client that missed deliveries can catch up. Endpoints can be managed by API (/webhook-endpoints, scope webhooks:manage) as well as in the admin portal. A client can rotate its own secrets (/client-secrets) and revoke its tokens (/oauth/revoke).

Rate limits. 600 requests a minute per client. Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset, and an X-Request-Id; over the limit, 429 with Retry-After. List pages hold up to 200 records (1,000 for /analytics/answers).

Credentials. OAuth 2.0 client credentials (oauth2) are the way in. Each client can hold two live secrets, so a secret is rotated without downtime: add a new secret on the organization's API page, switch to it, then revoke the old one. Older integrations may use an API key (apiKey, see security schemes); prefer OAuth clients for anything new.

Sync schedule. Catalogs change rarely: import hourly with updatedSince. Results: pull every few minutes, or on each result.updated webhook.

Timeouts. Calls normally answer in a second or two; the server allows up to 900 seconds. Use a 30-second client timeout and retry with exponential backoff.

Network. No IP allow-listing is needed in either direction: calls to Assess are public HTTPS secured by OAuth. If your firewall allow-lists the addresses you call, every *.assess.creatium.com host (production and staging) answers on 34.102.249.142. Webhook deliveries come from Google Cloud addresses that change, so verify the Creatium-Signature rather than the sender's IP. Fixed outbound IPs are available on request.

Sandbox. On staging (https://staging.app.assess.creatium.com/api/v1) a partner gets its own sandbox organization, with assessments to integrate against. There, POST /sandbox/attempts simulates a learner finishing (or starting) an assessment: the result appears in /results and result.updated and assignment.completed fire, as for a real learner. Ask support for one.

Service levels. The production API is available 99.5% of each calendar month, excluding maintenance announced 48 hours ahead. Live and past availability: https://status.assess.creatium.com, which checks GET /health every five minutes. Support answers within 24 hours; production down within 4 hours at the latest.

Changes in 1.2 to 1.6 (live 1 October 2026; all additive except these, which an existing client may notice): a path ID that isn't a UUID gives 400 (was 404); a cursor reused with other filters gives 400; unknown paths and wrong methods answer problem+json 404/405 (was an HTML page); /oauth/token without grant_type gives invalid_request; a NOT_STARTED record's resultId is now a UUID that its first attempt keeps (enr_… IDs still resolve); ResultStatus adds REMOVED; reportUrl is /report/{resultId}; an analytics team with fewer finished attempts than the organization's minimumGroupSize (default 5) is suppressed; an older attempt's lastUpdated moves when a retake flips its flags; during a webhook secret rotation Creatium-Signature carries two v1 values. The guide lists everything new, version by version.

Versioning. This is v1 (/api/v1). Fields and endpoints can be added at any time within a version. Breaking changes ship only in a new version (/api/v2), with at least 90 days' notice, and v1 keeps running alongside.

Data residency. Learner data is stored and processed in the United States (Google Cloud, US). Residency in other countries is available on request.

Support. Through a shared Slack group or support@creatium.com. Production down: we respond as soon as possible, and within 4 hours at the latest. Everything else: we respond within 24 hours. Escalation: Jisha Nambiar, jisha@creatium.com. Status: https://status.assess.creatium.com; incidents are also posted in the Slack group and emailed to your technical contact. Include the X-Request-Id of the call in question.

Authentication

OAuth 2.0 client credentials.

An org admin creates a client on the API page in Assess and copies its secret (shown once). Send the token as Authorization: Bearer <token> on every call.

curl -s https://app.assess.creatium.com/api/v1/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=client_credentials

Scopes

  • catalog:readRead the assessment catalog.
  • results:readRead learner attempts and results.
  • enrollment:readRead enrollments, assignments and people with their teams.
  • enrollment:writeEnroll and unenroll learners, and manage assignments and teams. Includes everything `enrollment:read` allows.
  • analytics:readRead analytics and export every answer.
  • analytics:answer-keysSee `correctAnswer` in the answer export when the organization's policy withholds it from other clients.
  • proctoring:readSee proctoring results when the organization's policy requires this scope.
  • webhooks:manageAdd, change, test and remove webhook endpoints, and rotate their secrets.
  • people:eraseErase a person's data (GDPR, CCPA). Never implied by other scopes or by API keys.

API keys (older integrations)

Legacy API keys (ak_…), created on the organization's API page and sent as Authorization: Bearer ak_…. A key carries every scope and stays valid until revoked. Prefer an OAuth client (oauth2) for new integrations.

POST/oauth/token

Get an access token

OAuth 2.0 client credentials grant (RFC 6749 §4.4). Send the client ID and secret with HTTP Basic authentication (preferred) or in the form body.

For a manager: token exchange (RFC 8693) with subject_token = the manager's personId or email. The token holds only catalog:read, results:read and analytics:read (of the client's scopes), and sees only the manager's reports. Under the policy's managerAccess: none refuses the exchange (unauthorized_client); aggregates allows analytics only (a manager whose reports are fewer than minimumGroupSize sees them suppressed); individual also allows their reports' results, progress, answers and profiles. It never reads /events or manages the client. An unknown person is invalid_grant. Tokens are opaque bearer tokens, valid for expires_in seconds (3600). Request a new token before expiry; refresh tokens are not issued.

Request body

Request body of Get an access token
FieldTypeDescription
grant_typerequired"client_credentials" | "urn:ietf:params:oauth:grant-type:token-exchange"

client_credentials for an organization-wide token; token exchange (RFC 8693) for a token acting for one manager.

subject_tokenstring

Token exchange only. The manager's personId or email.

subject_token_type"urn:creatium:params:oauth:token-type:person"

Token exchange only.

scopestring

Space-separated scopes. Omit for every scope granted to the client.

client_idstring

Only when not using HTTP Basic.

client_secretstring

Only when not using HTTP Basic.

Responses

  • 200

    Token issued.

    Token
  • 400

    Malformed request or unsupported grant type (invalid_request, unsupported_grant_type, invalid_scope).

    OAuthError
  • 401

    Unknown client or wrong secret (invalid_client).

    OAuthError
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
POST/oauth/revoke

Revoke an access token

Token revocation (RFC 7009). Authenticate as at /oauth/token (HTTP Basic) and send one of the client's own access tokens as token. Answers 200 whether or not the token was live.

Request body

Request body of Revoke an access token
FieldTypeDescription
tokenrequiredstring
token_type_hint"access_token"

Responses

  • 200

    Revoked (or was not live).

  • 400

    token missing, or not a form body (invalid_request).

    OAuthError
  • 401

    Unknown client or wrong secret (invalid_client).

    OAuthError
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
GET/client-secrets

List this client's secrets

The calling OAuth client's live secrets (last four characters only). Any scope. API keys have none.

Responses

  • 200

    The live secrets.

  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
POST/client-secrets

Add a secret (rotation)

A second live secret, shown once in this response. Switch your system to it, then delete the old one. A client holds at most two (409 secret_limit).

Responses

  • 201

    The new secret.

    ClientSecret
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 409

    The change clashes with another record: email_in_use, external_id_in_use or idempotency_conflict.

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
DELETE/client-secrets/{secretId}

Revoke a secret

Revokes one of the calling client's secrets. Never its last (409 secret_limit). Tokens already issued stay valid until they expire; revoke them with /oauth/revoke.

Parameters

Parameters of Revoke a secret
NameInTypeDescription
secretIdrequiredpathstring (uuid)

From GET /client-secrets.

Responses

  • 204

    Revoked.

  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 409

    The change clashes with another record: email_in_use, external_id_in_use or idempotency_conflict.

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem

Catalog

Assessments available to the organization's learners.

GET/assessmentscatalog:read

List assessments

The organization's catalog: assessments that are, or have been, available to learners. Drafts are never listed. For delta sync, send updatedSince with the lastUpdatedDate of the newest record from the previous run. Retired assessments are returned with status: Retired (not deleted), so the importer can hide them. Results are ordered by lastUpdatedDate, then assessmentId, ascending.

Parameters

Parameters of List assessments
NameInTypeDescription
updatedSincequerystring (date-time)

Only assessments whose lastUpdatedDate is at or after this instant.

updatedBeforequerystring (date-time)

Only assessments whose lastUpdatedDate is before this instant.

statusquery"Active" | "Retired" | "all"

Default all (Active and Retired), which delta sync needs to see retirements.

typequeryAssessmentType
languagequerystring

BCP 47 tag. Only assessments available in this language.

cursorquerystring

Opaque cursor from the previous page's pagination.nextCursor. Omit for the first page. Send the same filters as the request that issued it; other filters give 400.

limitqueryinteger

Page size.

Responses

  • 200

    One page of assessments.

  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
GET/assessments/{assessmentId}catalog:read

Get an assessment

One catalog record, live or retired; drafts are 404 assessment_not_found. Same shape as in the list.

Parameters

Parameters of Get an assessment
NameInTypeDescription
assessmentIdrequiredpathstring (uuid)

Stable assessment ID (UUID).

Responses

  • 200

    The assessment.

  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    The assessment ID does not exist in this organization, or is a draft (assessment_not_found).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
GET/assessments/{assessmentId}/questionscatalog:read

List an assessment's questions (metadata)

The live and retired questions of an assessment, ordered by questionId, for joining /analytics/answers. Never their text or answers.

Parameters

Parameters of List an assessment's questions (metadata)
NameInTypeDescription
assessmentIdrequiredpathstring (uuid)

Stable assessment ID (UUID).

cursorquerystring

Opaque cursor from the previous page's pagination.nextCursor. Omit for the first page. Send the same filters as the request that issued it; other filters give 400.

limitqueryinteger

Page size.

Responses

  • 200

    One page of questions.

  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    The assessment ID does not exist in this organization, or is a draft (assessment_not_found).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem

Results

Learner attempts, progress and outcomes (pull).

GET/resultsresults:read

List learner results

One record per learner per attempt, plus one NOT_STARTED record for each enrolled learner who has not yet started (attemptNumber 1, no dates). The date window is mandatory: send updatedSince (and optionally updatedBefore). For incremental sync, send the lastUpdated of the newest record from the previous run. Records are ordered by lastUpdated, then resultId, ascending, so a run can resume from its cursor.

A record changes (and its lastUpdated moves forward) when the attempt starts, progresses, is submitted, is re-scored (for example after a human review of an open answer, or a role play scored after submission), or when withheld results are released.

Latency: records are available within 60 seconds of the activity. Backfill: any past window, up to the organization's retention period, by setting updatedSince and updatedBefore.

Parameters

Parameters of List learner results
NameInTypeDescription
updatedSincerequiredquerystring (date-time)

Start of the window (inclusive).

updatedBeforequerystring (date-time)

End of the window (exclusive). Default now.

learnerEmailquerystring (email)

Only this learner. An email with no records returns an empty page, not an error.

assessmentIdquerystring (uuid)

Only this assessment. An unknown ID returns 404 assessment_not_found.

statusquerystring

Comma-separated statuses. Default all.

latestOnlyqueryboolean

Only each learner's latest attempt per assessment.

teamquerystring

Only people in this team (case-insensitive).

managerEmailquerystring (email)

Only this manager's reports (direct; with includeIndirect=true, every level below).

includeIndirectqueryboolean

With managerEmail, include reports of reports.

cursorquerystring

Opaque cursor from the previous page's pagination.nextCursor. Omit for the first page. Send the same filters as the request that issued it; other filters give 400.

limitqueryinteger

Page size.

Responses

  • 200

    One page of results.

  • 400

    The request is malformed, or the mandatory date window (updatedSince) is missing.

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    The assessment ID does not exist in this organization, or is a draft (assessment_not_found).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
GET/results/{resultId}results:read

Get one result

One record from /results, by resultId. Placeholder IDs from before 1.3.0 (enr_…) still resolve.

Parameters

Parameters of Get one result
NameInTypeDescription
resultIdrequiredpathstring

A resultId from /results.

Responses

  • 200

    The result.

  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem

Enrollment

Optional. Assign learners before launch. Launching from the catalog link assigns automatically.

GET/enrollmentsenrollment:readenrollment:write

List enrollments

Everyone enrolled in a live or retired assessment, by email or assessment, oldest first. source says how (API, invite, launch link or assignment).

Parameters

Parameters of List enrollments
NameInTypeDescription
learnerEmailquerystring (email)
assessmentIdquerystring (uuid)
cursorquerystring

Opaque cursor from the previous page's pagination.nextCursor. Omit for the first page. Send the same filters as the request that issued it; other filters give 400.

limitqueryinteger

Page size.

Responses

  • 200

    One page of enrollments.

  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
POST/enrollmentsenrollment:write

Enroll a learner

Optional. Assigns assessments to a learner ahead of launch, so they appear on the learner's Creatium home and in results as NOT_STARTED. No account needs to exist: the learner's account is created on first SSO sign-in. Idempotent: enrolling an already-enrolled learner succeeds without change. Send an Idempotency-Key to make retries safe. Returns the object itself, not wrapped in data.

Parameters

Parameters of Enroll a learner
NameInTypeDescription
Idempotency-Keyheaderstring

Any unique string, at most 255 characters. The same key with the same body returns the first response for 24 hours; with a different body, 409.

Request body EnrollmentRequest

Request body of Enroll a learner
FieldTypeDescription
emailrequiredstring (email)
firstNamestring

(up to 100 characters)

lastNamestring

(up to 100 characters)

assessmentIdsrequiredstring (uuid)[]

(up to 100 items)

notifyboolean

Send Creatium's invitation email. Leave false when the platform notifies learners itself. (default false)

{
  "email": "john.doe@example.com",
  "firstName": "John",
  "lastName": "Doe",
  "assessmentIds": [
    "5f1d6c1e-2a4b-4c8e-9f3a-7b6d2e1c0a91",
    "0b7e3d9a-6c21-4f58-8a1e-2d4c9b7f6e30"
  ],
  "notify": false
}

Responses

  • 207

    One outcome per assessment.

    EnrollmentResponse
  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 409

    The Idempotency-Key was used with a different body (idempotency_conflict).

    Problem
  • 422

    Well-formed but not allowed (for example an email outside the organization's allowed domains).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
POST/enrollments:batchenrollment:write

Enroll many people

Up to 1,000 people (10,000 person-assessment pairs) per call; each as POST /enrollments. A person outside the organization's email domains fails alone (email_outside_domains). Send an Idempotency-Key to make retries safe.

Parameters

Parameters of Enroll many people
NameInTypeDescription
Idempotency-Keyheaderstring

Any unique string, at most 255 characters. The same key with the same body returns the first response for 24 hours; with a different body, 409.

Request body BatchEnrollmentRequest

Request body of Enroll many people
FieldTypeDescription
enrollmentsrequiredobject[]

(up to 1000 items)

notifyboolean

Send Creatium's invitation email to each person newly enrolled. (default false)

Responses

  • 207

    One outcome per person and assessment.

    BatchEnrollmentResponse
  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 409

    The Idempotency-Key was used with a different body (idempotency_conflict).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
DELETE/enrollments/{assessmentId}/{learnerEmail}enrollment:write

Unenroll a learner

Removes the assignment. The learner can no longer start the assessment from their Creatium home. An attempt already in progress can be finished. Completed attempts and their results are kept and still returned by /results. Idempotent: removing an absent enrollment returns 204. A learner who has the assessment through an assignment gets 409 enrollment_from_assignment: remove them from the assignment instead.

Parameters

Parameters of Unenroll a learner
NameInTypeDescription
assessmentIdrequiredpathstring (uuid)

Stable assessment ID (UUID).

learnerEmailrequiredpathstring (email)

The person's personId, or their email address URL-encoded (@ as %40, matched case-insensitively). Prefer the personId; an email in a path appears in access logs.

Responses

  • 204

    Unenrolled (or was not enrolled).

  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    The assessment ID does not exist in this organization, or is a draft (assessment_not_found).

    Problem
  • 409

    The learner has this assessment through an assignment (enrollment_from_assignment). Remove them from the assignment instead.

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem

Assignments

Optional. A named set of assessments, with an optional due date and rules, given to people (by email) and teams. Everyone it reaches is enrolled in each of its assessments (source: ASSIGNMENT in /enrollments), including people who join a team later. Removing someone, or archiving the assignment, unenrolls them from what nothing else gives them; attempts in progress can finish and results are kept. Progress is per person across the whole set.

GET/assignmentsenrollment:readenrollment:write

List assignments

Live assignments, oldest change first (cursor paging on updatedAt).

Parameters

Parameters of List assignments
NameInTypeDescription
includeArchivedqueryboolean

Also list archived assignments (with archivedAt), so a client syncing by updatedAt learns of an archive.

learnerEmailquerystring (email)

Only assignments that reach this person, by email or through their team.

teamquerystring

Only assignments given to this team.

cursorquerystring

Opaque cursor from the previous page's pagination.nextCursor. Omit for the first page. Send the same filters as the request that issued it; other filters give 400.

limitqueryinteger

Page size.

Responses

  • 200

    One page of assignments.

  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
POST/assignmentsenrollment:write

Create an assignment

Gives a set of assessments to people and teams in one call. Assessments must be live, and the organization's own or shared down to it by its parent. With notify, each person newly reached gets one email listing the assessments. Send an Idempotency-Key to make retries safe. Returns the object itself, not wrapped in data.

Parameters

Parameters of Create an assignment
NameInTypeDescription
Idempotency-Keyheaderstring

Any unique string, at most 255 characters. The same key with the same body returns the first response for 24 hours; with a different body, 409.

Request body AssignmentRequest

Request body of Create an assignment
FieldTypeDescription
namerequiredstring

(up to 200 characters)

assessmentIdsrequiredstring (uuid)[]

In order. Live assessments only. (up to 50 items)

dueAtstring (date-time) | null

Shown on each person's home; a reminder goes out 48 hours before to anyone unfinished.

closeAtDueboolean

After dueAt nobody can start a new attempt. (default false)

orderedboolean

Each assessment opens once the ones before it are finished. (default false)

maxAttemptsinteger | null

Attempts allowed at each assessment; null for no limit. (1 to 20)

emailsstring (email)[]

(up to 1000 items)

teamsstring[]

(up to 50 items)

notifyboolean

(default false)

{
  "name": "New starter onboarding",
  "assessmentIds": [
    "5f1d6c1e-2a4b-4c8e-9f3a-7b6d2e1c0a91",
    "0b7e3d9a-6c21-4f58-8a1e-2d4c9b7f6e30"
  ],
  "dueAt": "2026-10-31T17:00:00Z",
  "ordered": true,
  "teams": [
    "Sales"
  ],
  "emails": [
    "john.doe@example.com"
  ],
  "notify": true
}

Responses

  • 201

    Created, with one outcome per member.

    Assignment
  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    The assessment ID does not exist in this organization, or is a draft (assessment_not_found).

    Problem
  • 409

    The Idempotency-Key was used with a different body (idempotency_conflict).

    Problem
  • 422

    Well-formed but not allowed (for example an email outside the organization's allowed domains).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
GET/assignments/{assignmentId}enrollment:readenrollment:write

Get an assignment

One live assignment, with its assessments in order and its members. Returns the object itself, not wrapped in data, with an ETag to send back as If-Match when changing it.

Parameters

Parameters of Get an assignment
NameInTypeDescription
assignmentIdrequiredpathstring (uuid)

Stable assignment ID (UUID).

Responses

  • 200

    The assignment.

    Assignment
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
PATCH/assignments/{assignmentId}enrollment:write

Change an assignment

Send only the fields to change. assessmentIds replaces the whole list (and its order); null clears dueAt or maxAttempts. Send the ETag from your last read as If-Match to refuse the change (412) if someone else changed it first. Returns the object itself, not wrapped in data, with its new ETag.

Parameters

Parameters of Change an assignment
NameInTypeDescription
assignmentIdrequiredpathstring (uuid)

Stable assignment ID (UUID).

If-Matchheaderstring

The ETag from your last read. When the record has changed since, 412 and nothing is changed.

Idempotency-Keyheaderstring

Any unique string, at most 255 characters. The same key with the same body returns the first response for 24 hours; with a different body, 409.

Request body AssignmentUpdate

Request body of Change an assignment
FieldTypeDescription
namestring

(up to 200 characters)

assessmentIdsstring (uuid)[]

In order. Live assessments only. (up to 50 items)

dueAtstring (date-time) | null

Shown on each person's home; a reminder goes out 48 hours before to anyone unfinished.

closeAtDueboolean

After dueAt nobody can start a new attempt. (default false)

orderedboolean

Each assessment opens once the ones before it are finished. (default false)

maxAttemptsinteger | null

Attempts allowed at each assessment; null for no limit. (1 to 20)

Responses

  • 200

    The changed assignment.

    Assignment
  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 409

    The Idempotency-Key was used with a different body (idempotency_conflict).

    Problem
  • 412

    If-Match no longer matches: someone changed the record since you read it (precondition_failed).

    Problem
  • 422

    Well-formed but not allowed (for example an email outside the organization's allowed domains).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
DELETE/assignments/{assignmentId}enrollment:write

Archive an assignment

Nobody new can start from it. Attempts in progress can finish; results are kept. Idempotent.

Parameters

Parameters of Archive an assignment
NameInTypeDescription
assignmentIdrequiredpathstring (uuid)

Stable assignment ID (UUID).

Responses

  • 204

    Archived (or already gone).

  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
POST/assignments/{assignmentId}/membersenrollment:write

Give an assignment to more people and teams

Up to 1,000 emails and 50 teams per call. With notify, each person newly reached gets one email. Returns the object itself, not wrapped in data.

Parameters

Parameters of Give an assignment to more people and teams
NameInTypeDescription
assignmentIdrequiredpathstring (uuid)

Stable assignment ID (UUID).

Idempotency-Keyheaderstring

Any unique string, at most 255 characters. The same key with the same body returns the first response for 24 hours; with a different body, 409.

Request body MembersRequest

Request body of Give an assignment to more people and teams
FieldTypeDescription
emailsstring (email)[]

(up to 1000 items)

teamsstring[]

(up to 50 items)

notifyboolean

(default false)

Responses

  • 207

    One outcome per member.

  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 409

    The Idempotency-Key was used with a different body (idempotency_conflict).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
DELETE/assignments/{assignmentId}/members/{learnerEmail}enrollment:write

Take a person out of an assignment

They keep anything a team or another assignment still gives them. Idempotent.

Parameters

Parameters of Take a person out of an assignment
NameInTypeDescription
assignmentIdrequiredpathstring (uuid)

Stable assignment ID (UUID).

learnerEmailrequiredpathstring (email)

The person's personId, or their email address URL-encoded (@ as %40, matched case-insensitively). Prefer the personId; an email in a path appears in access logs.

Responses

  • 204

    Removed (or was not a member).

  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
DELETE/assignments/{assignmentId}/teams/{team}enrollment:write

Take a team out of an assignment

Team names match case-insensitively. Idempotent.

Parameters

Parameters of Take a team out of an assignment
NameInTypeDescription
assignmentIdrequiredpathstring (uuid)

Stable assignment ID (UUID).

teamrequiredpathstring

The team's name, URL-encoded; matched case-insensitively.

Responses

  • 204

    Removed (or was not given to that team).

  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
GET/assignments/{assignmentId}/progressresults:read

Progress per person

One record per person the assignment reaches, ordered by email, from their latest attempt at each assessment. Worked out on each request, never cached.

Parameters

Parameters of Progress per person
NameInTypeDescription
assignmentIdrequiredpathstring (uuid)

Stable assignment ID (UUID).

learnerEmailquerystring (email)
statusquerystring

Comma-separated ProgressStatus values.

overduequeryboolean
teamquerystring

Only people in this team (case-insensitive).

managerEmailquerystring (email)

Only this manager's reports (direct; with includeIndirect=true, every level below).

includeIndirectqueryboolean

With managerEmail, include reports of reports.

cursorquerystring

Opaque cursor from the previous page's pagination.nextCursor. Omit for the first page. Send the same filters as the request that issued it; other filters give 400.

limitqueryinteger

Page size.

Responses

  • 200

    One page of progress records.

  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem

People

Optional. Each person's team (one per person), which team assignments follow.

GET/peopleenrollment:readenrollment:write

List people and their teams

Everyone in the organization (candidates and staff) with their team, oldest first.

Parameters

Parameters of List people and their teams
NameInTypeDescription
teamquerystring
cursorquerystring

Opaque cursor from the previous page's pagination.nextCursor. Omit for the first page. Send the same filters as the request that issued it; other filters give 400.

limitqueryinteger

Page size.

Responses

  • 200

    One page of people.

  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
PUT/people/{learnerEmail}enrollment:write

Set a person's team or employee number

Sets any of team (one per person; null clears it), externalId, managerEmail, department and location; fields left out stay as they are. Someone not in the organization yet is added as a candidate (inside its email domains only). Assignments given to the team follow at once. Returns the object itself, not wrapped in data.

Parameters

Parameters of Set a person's team or employee number
NameInTypeDescription
learnerEmailrequiredpathstring (email)

The person's personId, or their email address URL-encoded (@ as %40, matched case-insensitively). Prefer the personId; an email in a path appears in access logs.

Idempotency-Keyheaderstring

Any unique string, at most 255 characters. The same key with the same body returns the first response for 24 hours; with a different body, 409.

Request body PersonUpdate

Request body of Set a person's team or employee number
FieldTypeDescription
teamstring | null

(up to 100 characters)

externalIdstring | null

(up to 200 characters)

managerEmailstring (email) | null

Not the person themselves.

departmentstring | null

(up to 100 characters)

locationstring | null

(up to 100 characters)

Responses

  • 200

    The person.

    Person
  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 409

    The change clashes with another record: email_in_use, external_id_in_use or idempotency_conflict.

    Problem
  • 422

    Well-formed but not allowed (for example an email outside the organization's allowed domains).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
DELETE/people/{learnerEmail}enrollment:write

Deactivate a leaver

Removes the person's candidate role, assignment memberships and enrollments (each NOT_STARTED record comes back once as REMOVED). Finished results stay. Idempotent. Someone who signs in or is enrolled again becomes active again. Staff are refused (422): manage them in the admin portal.

Parameters

Parameters of Deactivate a leaver
NameInTypeDescription
learnerEmailrequiredpathstring (email)

The person's personId, or their email address URL-encoded (@ as %40, matched case-insensitively). Prefer the personId; an email in a path appears in access logs.

Responses

  • 204

    Deactivated (or already).

  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 422

    Well-formed but not allowed (for example an email outside the organization's allowed domains).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
POST/people/{personId}:changeEmailenrollment:write

Change a person's email address

Moves the person's role, team, enrollments, attempts and assignment memberships to the new address. The personId and every resultId stay the same; their records' lastUpdated moves. Returns the Person itself.

Parameters

Parameters of Change a person's email address
NameInTypeDescription
personIdrequiredpathstring (uuid)

The person's personId (UUID).

Idempotency-Keyheaderstring

Any unique string, at most 255 characters. The same key with the same body returns the first response for 24 hours; with a different body, 409.

Request body

Request body of Change a person's email address
FieldTypeDescription
emailrequiredstring (email)

(up to 320 characters)

Responses

  • 200

    The person, under the new address.

    Person
  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 409

    The change clashes with another record: email_in_use, external_id_in_use or idempotency_conflict.

    Problem
  • 422

    Well-formed but not allowed (for example an email outside the organization's allowed domains).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
POST/people/{personId}:erasepeople:erase

Erase a person (GDPR, CCPA)

Cannot be undone. Deactivates the person, then removes what identifies them in this organization: their answers' content, proctoring readings, certificates, settings, and the person record. Their attempts stay only as anonymous rows (learnerEmail becomes erased-<personId>@erased.invalid, personId null, scores kept) so organization-wide numbers don't change; their lastUpdated moves, so your next sync overwrites your copy. Completes within the request and then sends person.erased. Needs the people:erase scope, which API keys never have.

Parameters

Parameters of Erase a person (GDPR, CCPA)
NameInTypeDescription
personIdrequiredpathstring (uuid)

The person's personId (UUID).

Responses

  • 200

    Erased.

    Erasure
  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 422

    Well-formed but not allowed (for example an email outside the organization's allowed domains).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
POST/people:batchenrollment:write

Update many people (HRIS sync)

Up to 1,000 people, each as PUT /people/{learnerEmail}: team and attributes, adding anyone new (inside the organization's email domains). Reporting lines are set after everyone in the call exists, so a manager and their reports can arrive together. A row that fails fails alone.

Parameters

Parameters of Update many people (HRIS sync)
NameInTypeDescription
Idempotency-Keyheaderstring

Any unique string, at most 255 characters. The same key with the same body returns the first response for 24 hours; with a different body, 409.

Request body PeopleBatchRequest

Request body of Update many people (HRIS sync)
FieldTypeDescription
peoplerequiredPersonUpdate[]

(up to 1000 items)

Responses

  • 207

    One outcome per person.

    PeopleBatchResponse
  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 409

    The Idempotency-Key was used with a different body (idempotency_conflict).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
GET/people/{learnerEmail}/profileresults:read

A person's performance across assessments

Their latest released level per skill (with when, where it was measured, and the change from the level before), their assignments with status and overdue flags, and their certificates. A manager's token reaches only their reports, and only when the policy's managerAccess is individual.

Parameters

Parameters of A person's performance across assessments
NameInTypeDescription
learnerEmailrequiredpathstring (email)

The person's personId, or their email address URL-encoded (@ as %40, matched case-insensitively). Prefer the personId; an email in a path appears in access logs.

Responses

  • 200

    The profile.

  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
GET/organization/policy

The organization's data policy

What clients and managers' tokens may see. Set by an organization admin in Admin → API. Any scope.

Responses

  • 200

    The policy.

  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem

Webhooks

Optional push delivery of result, enrollment, assignment, catalog and erasure events.

EVENTresult.updated

A result was created or changed

Optional push delivery, alongside pull. Sent when an attempt starts, is submitted or is re-scored, and when withheld results are released. Configure the endpoint on the organization's API page in the Creatium admin portal.

  • Signature: Creatium-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>. Reject deliveries older than 5 minutes. For 24 hours after a secret is rotated the header carries a second v1 signed with the old secret: accept the delivery if any v1 matches.
  • Idempotency: Creatium-Event-Id (also id in the body) is unique per event. Deduplicate on it.
  • Ordering: not guaranteed. Apply a record only if its lastUpdated is newer than the one held.
  • Retries: any non-2xx response or a timeout after 10 seconds is retried with exponential backoff (1 min, 5 min, 30 min, 2 h, 6 h, 12 h), for up to 24 hours. Every delivery attempt, including retries, is re-signed with a fresh timestamp; the body and Creatium-Event-Id stay the same.
  • Size: one result per event; bodies stay under 64 KB.
  • Missed events: the same events are at GET /events for 30 days.

Body ResultEvent

result.updated body
FieldTypeDescription
idrequiredstring (uuid)
typerequired"result.updated"
createdAtrequiredstring (date-time)
datarequiredResult
EVENTassignment.completed

A person finished every assessment in an assignment

Sent once per person and assignment, when their last assessment in it is submitted or times out. Signed, deduplicated and retried exactly like result.updated.

Body AssignmentEvent

assignment.completed body
FieldTypeDescription
idrequiredstring (uuid)
typerequired"assignment.completed"
createdAtrequiredstring (date-time)
datarequiredAssignmentProgress
EVENTenrollment.created

Someone was enrolled

Someone was enrolled in a live assessment, however it happened (API, invite, launch link or assignment). Signed, deduplicated and retried exactly like result.updated.

Body

enrollment.created body
FieldTypeDescription
idrequiredstring (uuid)

Same as the Creatium-Event-Id header.

typerequired"result.updated" | "assignment.completed" | "enrollment.created" | "enrollment.removed" | "assignment.updated" | "assignment.archived" | "assessment.updated" | "person.erased"
createdAtrequiredstring (date-time)
datarequiredEnrollmentEventData
EVENTenrollment.removed

An enrollment was taken away

Unenrolled, removed from an assignment, or deactivated. With resultId when a REMOVED record now replaces their NOT_STARTED one. Signed, deduplicated and retried exactly like result.updated.

Body

enrollment.removed body
FieldTypeDescription
idrequiredstring (uuid)

Same as the Creatium-Event-Id header.

typerequired"result.updated" | "assignment.completed" | "enrollment.created" | "enrollment.removed" | "assignment.updated" | "assignment.archived" | "assessment.updated" | "person.erased"
createdAtrequiredstring (date-time)
datarequiredEnrollmentEventData
EVENTassignment.updated

An assignment changed

Its settings, assessments or members changed. Signed, deduplicated and retried exactly like result.updated.

Body

assignment.updated body
FieldTypeDescription
idrequiredstring (uuid)

Same as the Creatium-Event-Id header.

typerequired"result.updated" | "assignment.completed" | "enrollment.created" | "enrollment.removed" | "assignment.updated" | "assignment.archived" | "assessment.updated" | "person.erased"
createdAtrequiredstring (date-time)
datarequiredAssignmentEventData
EVENTassignment.archived

An assignment was archived

Nobody new can start from it; attempts in progress can finish. Signed, deduplicated and retried exactly like result.updated.

Body

assignment.archived body
FieldTypeDescription
idrequiredstring (uuid)

Same as the Creatium-Event-Id header.

typerequired"result.updated" | "assignment.completed" | "enrollment.created" | "enrollment.removed" | "assignment.updated" | "assignment.archived" | "assessment.updated" | "person.erased"
createdAtrequiredstring (date-time)
datarequiredAssignmentEventData
EVENTassessment.updated

A catalog assessment changed

A live assessment was published or changed, or was retired. Signed, deduplicated and retried exactly like result.updated.

Body

assessment.updated body
FieldTypeDescription
idrequiredstring (uuid)

Same as the Creatium-Event-Id header.

typerequired"result.updated" | "assignment.completed" | "enrollment.created" | "enrollment.removed" | "assignment.updated" | "assignment.archived" | "assessment.updated" | "person.erased"
createdAtrequiredstring (date-time)
datarequiredAssessmentEventData
EVENTperson.erased

A person was erased

Sent once POST /people/{personId}:erase finishes. Delete everything you hold about that personId. Signed, deduplicated and retried exactly like result.updated.

Body ErasureEvent

person.erased body
FieldTypeDescription
idrequiredstring (uuid)
typerequired"person.erased"
createdAtrequiredstring (date-time)
datarequiredobject
data.personIdrequiredstring (uuid)
data.erasedAtrequiredstring (date-time)
EVENTassessment.completedDeprecated

An attempt was submitted (deprecated)

Deprecated: use result.updated, which is timestamped, retried and carries the full result. assessment.completed stops on 31 January 2027.

Sent once when an attempt is submitted, delivered once without retries. Signed with x-assess-signature: sha256=<hex HMAC-SHA256(secret, raw body)> (no timestamp).

Body

assessment.completed body
FieldTypeDescription
idrequiredstring (uuid)
typerequired"assessment.completed"
createdAtrequiredstring (date-time)
datarequiredobject
data.attemptIdrequiredstring (uuid)
data.assessmentIdrequiredstring (uuid)
data.assessmentTitlestring
data.candidaterequiredobject
data.candidate.emailrequiredstring (email)
data.statusrequired"submitted" | "timeout"
data.scorenumber | null
data.levelsobject
data.submittedAtrequiredstring (date-time) | null
data.channel"web" | "embed" | "scorm" | "api"

Operations

Health checks and the staging sandbox.

GET/health

Is the API up?

No token needed. 200 when the API can reach its database; 503 otherwise. The status page probes it every five minutes.

Responses

  • 200

    Up.

  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
POST/sandbox/attemptsenrollment:write

Simulate an attempt (sandbox only)

Only in a sandbox organization on staging; everywhere else 404. Enrolls the learner if needed, then records an attempt with the outcome asked for, scored against the assessment's own skill targets and pass mark (or the levels you send). As for a real learner, the record appears in /results (taking the NOT_STARTED record's resultId), result.updated fires, and assignment.completed fires when it finishes an assignment. Returns the Result.

Request body

Request body of Simulate an attempt (sandbox only)
FieldTypeDescription
learnerEmailrequiredstring (email)
assessmentIdrequiredstring (uuid)
outcome"passed" | "failed" | "in_progress"

(default "passed")

levelsobject

A level (1–10) per skill name, instead of what outcome implies.

Responses

  • 201

    The simulated attempt's Result.

    Result
  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem

Events

The last 30 days of events to replay after an outage, and webhook endpoints managed by API.

GET/eventsresults:readenrollment:readenrollment:writecatalog:read

List recent events

The last 30 days of events, oldest first, exactly as webhook endpoints receive them, so a client that missed deliveries can catch up. Only the types the client's scopes may read: result.updated and assignment.completed need results:read; enrollment, assignment and person.erased events need enrollment:read; assessment.updated needs catalog:read. Only organizations with an API client, key or endpoint keep events.

Parameters

Parameters of List recent events
NameInTypeDescription
typequerystring

Comma-separated event types.

createdSincequerystring (date-time)

Only events at or after this time (ISO 8601 UTC).

cursorquerystring

Opaque cursor from the previous page's pagination.nextCursor. Omit for the first page. Send the same filters as the request that issued it; other filters give 400.

limitqueryinteger

Page size.

Responses

  • 200

    One page of events.

  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
GET/webhook-endpointswebhooks:manage

List webhook endpoints

The organization's endpoints, oldest first (never their secrets).

Responses

  • 200

    The endpoints.

  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
POST/webhook-endpointswebhooks:manage

Add a webhook endpoint

A public https URL and the events it takes. The signing secret is in this response only.

Parameters

Parameters of Add a webhook endpoint
NameInTypeDescription
Idempotency-Keyheaderstring

Any unique string, at most 255 characters. The same key with the same body returns the first response for 24 hours; with a different body, 409.

Request body WebhookEndpointRequest

Request body of Add a webhook endpoint
FieldTypeDescription
urlrequiredstring (uri)
eventsrequiredstring[]

Responses

  • 201

    Added, with its secret.

    WebhookEndpoint
  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 409

    The Idempotency-Key was used with a different body (idempotency_conflict).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
GET/webhook-endpoints/{endpointId}webhooks:manage

Get a webhook endpoint

One endpoint (never its secret).

Parameters

Parameters of Get a webhook endpoint
NameInTypeDescription
endpointIdrequiredpathstring (uuid)

The endpoint's endpointId.

Responses

  • 200

    The endpoint.

    WebhookEndpoint
  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
PATCH/webhook-endpoints/{endpointId}webhooks:manage

Change a webhook endpoint

Send url, events or both; events replaces the list.

Parameters

Parameters of Change a webhook endpoint
NameInTypeDescription
endpointIdrequiredpathstring (uuid)

The endpoint's endpointId.

Request body WebhookEndpointRequest

Request body of Change a webhook endpoint
FieldTypeDescription
urlrequiredstring (uri)
eventsrequiredstring[]

Responses

  • 200

    The changed endpoint.

    WebhookEndpoint
  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
DELETE/webhook-endpoints/{endpointId}webhooks:manage

Remove a webhook endpoint

Idempotent. Undelivered events for it are dropped; they stay at GET /events.

Parameters

Parameters of Remove a webhook endpoint
NameInTypeDescription
endpointIdrequiredpathstring (uuid)

The endpoint's endpointId.

Responses

  • 204

    Removed (or already gone).

  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
POST/webhook-endpoints/{endpointId}:rotateSecretwebhooks:manage

Rotate an endpoint's secret

A new signing secret, in this response only. For 24 hours deliveries carry signatures with both the new and the old secret (two v1 values), so you can switch without missing any.

Parameters

Parameters of Rotate an endpoint's secret
NameInTypeDescription
endpointIdrequiredpathstring (uuid)

The endpoint's endpointId.

Responses

  • 200

    The endpoint, with its new secret.

    WebhookEndpoint
  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
POST/webhook-endpoints/{endpointId}:testwebhooks:manage

Send a test event

Sends a signed webhook.test event (data.endpointId) to this endpoint now and says how it answered. Not kept at /events.

Parameters

Parameters of Send a test event
NameInTypeDescription
endpointIdrequiredpathstring (uuid)

The endpoint's endpointId.

Responses

  • 200

    How the endpoint answered.

  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    No such resource in this organization.

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem

Analytics

For dashboards, BI tools and data warehouses. Computed tiles per assessment, and every answer as paged, incremental records (JSON or CSV) with keys that join to results, assessments and questions.

GET/analytics/assessmentsanalytics:read

Key numbers for every assessment

One summary per live or retired assessment, ordered by assessmentId. Rates are 0–1. Without limit, every assessment in one response; with it, pages of that size.

Parameters

Parameters of Key numbers for every assessment
NameInTypeDescription
fromquerystring (date-time)

Only attempts finished at or after this time (ISO 8601 UTC); attempts in progress count if started before to.

toquerystring (date-time)

Only attempts finished before this time.

teamquerystring

Only people in this team (case-insensitive).

managerEmailquerystring (email)

Only this manager's reports (direct; with includeIndirect=true, every level below).

includeIndirectqueryboolean

With managerEmail, include reports of reports.

cursorquerystring

Opaque cursor from the previous page's pagination.nextCursor. Omit for the first page. Send the same filters as the request that issued it; other filters give 400.

limitqueryinteger

Page size. Omit to get every assessment at once.

Responses

  • 200

    Summaries.

  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
GET/analytics/assessments/{assessmentId}analytics:read

Analytics for one assessment

The numbers behind the Designer's Analytics page: completion, the share of people meeting each skill's target and the spread of bands, the same by team, how often each question is answered correctly, and completions per day. One row per enrolled person, on their latest finished attempt. A team or group with fewer finished attempts than the policy's minimumGroupSize is listed with suppressed: true and no rates.

Parameters

Parameters of Analytics for one assessment
NameInTypeDescription
assessmentIdrequiredpathstring (uuid)

Stable assessment ID (UUID).

languagequerystring

Only people who took it in this language (BCP 47). Ignored when the assessment is not offered in it.

assignmentIdquerystring (uuid)

Only the people this assignment reaches (it must include the assessment).

fromquerystring (date-time)

Only attempts finished at or after this time (ISO 8601 UTC); attempts in progress count if started before to.

toquerystring (date-time)

Only attempts finished before this time.

teamquerystring

Only people in this team (case-insensitive).

managerEmailquerystring (email)

Only this manager's reports (direct; with includeIndirect=true, every level below).

includeIndirectqueryboolean

With managerEmail, include reports of reports.

groupByquery"team" | "department" | "location" | "manager"

Adds groups, the share meeting each skill's target per team, department, location or manager.

Responses

  • 200

    The assessment's analytics.

  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 404

    The assessment ID does not exist in this organization, or is a draft (assessment_not_found).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem
GET/analytics/answersanalytics:read

Every answer (for BI tools and warehouses)

One record per answer across the organization's assessments, with the keys to join them to results (resultId), assessments (assessmentId), questions (questionId) and people. Records are ordered by their attempt's lastUpdated, then attempt, then question, so an incremental sync can resume from nextCursor or restart from the newest lastUpdated it stored. A record comes back when its attempt changes (progress, submit, later scoring, released results). format=csv returns the same records as CSV; the next page's cursor is then in X-Next-Cursor.

Parameters

Parameters of Every answer (for BI tools and warehouses)
NameInTypeDescription
updatedSincequerystring (date-time)

Only answers in attempts updated at or after this instant.

updatedBeforequerystring (date-time)
assessmentIdquerystring (uuid)
peoplequery"email" | "anonymous"

email (default) or anonymous: a stable personId (C-…) instead of learnerEmail.

formatquery"json" | "csv"
teamquerystring

Only people in this team (case-insensitive).

managerEmailquerystring (email)

Only this manager's reports (direct; with includeIndirect=true, every level below).

includeIndirectqueryboolean

With managerEmail, include reports of reports.

cursorquerystring

Opaque cursor from the previous page's pagination.nextCursor. Omit for the first page. Send the same filters as the request that issued it; other filters give 400.

limitqueryinteger

Responses

  • 200

    One page of answers.

  • 400

    The request is malformed (a parameter or the body is not valid).

    Problem
  • 401

    Missing, expired or invalid access token.

    Problem
  • 403

    The token lacks the scope this endpoint needs (insufficient_scope).

    Problem
  • 429

    Rate limit exceeded (rate_limited). Retry after Retry-After seconds.

    Problem
  • 5XX

    internal_error (500) or service_unavailable (503, with Retry-After). Safe to retry GET requests with backoff.

    Problem

Errors

Every error is an RFC 9457 problem with a stable code; its type links here. Branch on the code, not the wording. The token endpoint uses OAuth's own shape (error, error_description).

invalid_request

400. A parameter or the body is not valid; errors[] names the fields. Fix the request.

invalid_token

401. The access token is missing, expired, revoked or wrong. Get a new token.

insufficient_scope

403. The client lacks the scope this operation needs. Ask the organization admin to add it.

not_found

404. No such resource in this organization (or no such path).

method_not_allowed

405. The path exists but not with that method; the Allow header lists the ones it takes.

assessment_not_found

404. The assessment ID does not exist in this organization, or is a draft.

assessment_not_active

422. The assessment is retired and takes no new enrollments or assignments.

idempotency_conflict

409. The same Idempotency-Key was sent with a different body in the last 24 hours.

enrollment_from_assignment

409. The person has the assessment through an assignment; remove them from the assignment instead.

email_in_use

409. Another person in the organization already has that email address.

external_id_in_use

409. Another person in the organization already has that externalId.

secret_limit

409. A client has at most two live secrets, and never fewer than one: revoke the old one first, or add a new one first.

precondition_failed

412. If-Match no longer matches the record. Read it again, then retry your change.

unprocessable

422. Well-formed but not allowed, for example an email outside the organization's domains.

rate_limited

429. More than 600 requests in a minute. Wait Retry-After seconds.

internal_error

500. Something failed on our side. Retry with backoff; quote the requestId if it persists.

service_unavailable

503. Temporarily unavailable. Retry after Retry-After seconds.

Schemas

Every object the API sends or accepts. New fields can appear at any time within a version, so ignore ones you don’t recognise.

Token

Token fields
FieldTypeDescription
access_tokenrequiredstring
token_typerequired"Bearer"
expires_inrequiredinteger

Seconds (3600).

scoperequiredstring
issued_token_type"urn:ietf:params:oauth:token-type:access_token"

Token exchange only.

OAuthError

OAuthError fields
FieldTypeDescription
errorrequired"invalid_request" | "invalid_client" | "invalid_grant" | "unauthorized_client" | "unsupported_grant_type" | "invalid_scope"
error_descriptionstring

Pagination

Cursor pagination. Keep requesting with nextCursor until hasMore is false (nextCursor is then null).

Pagination fields
FieldTypeDescription
limitrequiredinteger
hasMorerequiredboolean

False on the final page.

nextCursorrequiredstring | null
totalCountinteger

Records matching the filters across all pages. Omitted where counting would be costly (/analytics/answers).

AssessmentType"Skill" | "Certification"

Skill: finds each learner's level in each skill (a diagnostic). Certification: passing issues a verifiable certificate.

Band"Foundational" | "Intermediate" | "Advanced" | "Expert"

Proficiency bands on the 1–10 level scale: Foundational 1–3, Intermediate 4–5, Advanced 6–8, Expert 9–10.

Assessment

Assessment fields
FieldTypeDescription
assessmentIdrequiredstring (uuid)

Stable; identical in results; never reused.

titlerequiredstring

(up to 200 characters)

descriptionrequiredstring

Plain text.

descriptionFormatrequired"text"
objectivestring | null

The learning outcome the assessment measures.

launchUrlrequiredstring (uri)

Deep link (/launch/{assessmentId}) that starts SP-initiated SAML SSO with the organization's identity provider, then opens the assessment's start page and assigns it. A learner already signed in goes straight there. It is stable; no token is embedded. An ID that is unknown, not yet published, or not a UUID shows a 404 page before any sign-in. Add ?context=<up to 256 characters> (for example your enrollment ID; returned on the result as launchContext) and &returnUrl=<https URL> (a Back button on the results page once they finish; only to the organization's allowed LMS domains, otherwise ignored).

launchModerequired"SP_INITIATED_SSO"
durationrequiredinteger

Minutes. The time limit when timed, otherwise the typical time.

durationUnitrequired"minutes"
durationIsorequiredstring

ISO 8601 duration, e.g. PT45M.

timedboolean

True when the duration is a hard time limit.

statusrequired"Active" | "Retired"

Active: learners can launch it. Retired: closed to new attempts; results remain available.

typerequiredAssessmentType
levelrequiredBand

Complexity. The highest target band set across the assessment's skills.

adaptivetrue

Questions adapt to the learner, and the assessment ends once every skill's level is established. The number of questions varies.

questionCountobject
questionCount.maxrequiredinteger

Most knowledge-check questions an attempt can include.

questionCount.rolePlaysinteger

Role plays in each attempt.

questionCount.projectsinteger

Projects in each attempt.

maxScorerequired100

Scores are percentages.

passingScorerequirednumber | null

The pass mark, when the assessment passes on score. Null when it passes on skill targets (see passingRule). (0 to 100)

passingRulerequired"SCORE" | "SKILL_TARGETS"

SCORE: passed when score ≥ passingScore. SKILL_TARGETS: passed when every skill reaches its targetLevel.

languagesrequiredstring[]

BCP 47 tags (ISO 639-1 language, optional ISO 3166 region), e.g. en, ar-EG.

skillsrequiredstring[]

Names of the skills assessed.

skillDetailsSkillDetail[]
sectionsstring[]

The assessment's sections. Each skill is a section.

maxAttemptsinteger

Attempts a learner may take. No question, role play or project repeats across them.

publishedDatestring (date-time) | null
activationDatestring (date-time) | null

When it became available to learners.

expiryDatestring (date-time) | null

When it was retired; null while active.

lastUpdatedDaterequiredstring (date-time)

Changes on any change to a catalog field. Use for delta sync.

industrystring | null
linkedCoursesstring[]

Reserved. Always empty in v1.

thumbnailUrlstring (uri) | null

16:9 card image, public HTTPS.

ratingnull

Not collected in v1.

benchmarksobject

SkillDetail

SkillDetail fields
FieldTypeDescription
skillIdrequiredstring
namerequiredstring
targetLevelrequiredinteger

(1 to 10)

targetBandrequiredBand
objectivesstring[]

Benchmarks

Aggregates across the organization's completed attempts. Present only with 20 or more completions.

Benchmarks fields
FieldTypeDescription
completionsrequiredinteger
medianScorerequirednumber
passRaterequirednumber

(0 to 1)

ResultStatus"NOT_STARTED" | "IN_PROGRESS" | "AWAITING_RESULTS" | "PASSED" | "FAILED" | "REMOVED"

NOT_STARTED: enrolled, no attempt yet. IN_PROGRESS: started, not submitted. AWAITING_RESULTS: submitted; the organization releases results later. PASSED: submitted; pass threshold met. FAILED: submitted; below threshold. REMOVED: the enrollment was taken away before any attempt (unenrolled, removed from the assignment, or the person deactivated). Returned for 30 days, under the NOT_STARTED record's resultId, so an incremental sync learns of it. Delete your copy.

Result

Result fields
FieldTypeDescription
resultIdrequiredstring

Stable per attempt; re-scoring updates the same record. A NOT_STARTED record keeps its resultId when the learner starts: it becomes their first attempt.

personIdstring (uuid) | null

The person; survives an email change. Null once the person is erased (learnerEmail is then erased-<personId>@erased.invalid).

learnerEmailrequiredstring (email)

Lower case. Matches the SAML NameID.

teamstring | null
managerEmailstring (email) | null
learnerNamestring | null
assessmentIdrequiredstring (uuid)
attemptNumberrequiredinteger

1 for the first attempt, 2 for the first retake, and so on.

isLatestAttemptboolean
isBestAttemptboolean

The completed attempt with the highest score for this learner and assessment.

statusrequiredResultStatus
completionReason"SUBMITTED" | "TIME_EXPIRED" | null

Set once the attempt is submitted.

scorenumber | null

Percentage, 1 decimal place. Set when PASSED or FAILED. (0 to 100)

scoreType"PERCENTAGE"
maxScorerequired100
passingScorenumber | null

The pass mark applied, when passingRule is SCORE.

passingRulerequired"SCORE" | "SKILL_TARGETS"
result"PASS" | "FAIL" | null

Set when PASSED or FAILED.

startedDatestring (date-time) | null

Null only when NOT_STARTED.

completedDatestring (date-time) | null

When submitted.

progressPercentinteger | null

While IN_PROGRESS. (0 to 100)

gradestring | null

Overall proficiency, the band of the learner's lowest measured skill.

proficiencySkillResult[]

One entry per skill.

strongSkillsstring[]

Skills at or above their target level.

weakSkillsstring[]

Skills below their target level.

sectionScoresSectionScore[]
reportUrlstring (uri) | null

The result's report page (/report/{resultId}). Permanent link; it opens after sign-in for the learner, the organization's staff, and (when the policy's managerAccess is individual) the learner's managers, direct or above. Null until the attempt is scored. Print it for a PDF.

certificateUrlstring (uri) | null

Public, verifiable credential page (Certifier), when issued.

certificateIssuedAtstring (date-time) | null

When the certificate was issued.

certificateExpiresAtstring (date-time) | null

When it expires; null when it doesn't.

badgeUrlstring (uri) | null

Digital badge, when issued.

proctoringobject

Null when the attempt wasn't proctored (proctoring off, or the learner was excused), or when the organization's policy shares proctoring only with clients holding proctoring:read.

launchContextstring | null

The context the attempt was launched with (launchUrl?context=…); null otherwise. (up to 256 characters)

lastUpdatedrequiredstring (date-time)

Moves forward on every change to the record, including when a newer attempt or score flips this one's isLatestAttempt or isBestAttempt.

Proctoring

Camera proctoring. An AI model reads stills from the learner's camera (none is kept) and the browser notes what the learner does; likelihood combines them. It is a reason for a person to review, not proof: act on verdict, which a reviewer sets.

Proctoring fields
FieldTypeDescription
moderequired"CAMERA" | "CAMERA_FULLSCREEN"
likelihoodrequiredinteger | null

Cheating likelihood, 0–100. Null while the attempt is in progress. (0 to 100)

levelrequired"LOW" | "MEDIUM" | "HIGH" | null

LOW under 25, MEDIUM 25–60, HIGH over 60.

flagsrequiredobject[]
verdictrequired"CLEARED" | "CONFIRMED" | null

A reviewer's decision; a change fires result.updated.

reviewUrlrequiredstring (uri)

The attempt's review page in Creatium Assess (staff sign-in).

SkillResult

SkillResult fields
FieldTypeDescription
skillrequiredstring
levelrequiredinteger | null

Null: not enough evidence to give a level with confidence. (1 to 10)

bandrequiredobject
confidencenumber | null

Probability the true level is within one of level. A level is given only at 0.8 or above. (0 to 1)

targetLevelrequiredinteger

(1 to 10)

metrequiredboolean

SectionScore

SectionScore fields
FieldTypeDescription
sectionrequiredstring

Skill name.

scorerequirednumber

Percentage of the section's available points earned.

maxScorerequired100

ResultEvent

ResultEvent fields
FieldTypeDescription
idrequiredstring (uuid)
typerequired"result.updated"
createdAtrequiredstring (date-time)
datarequiredResult

AnalyticsSummary

AnalyticsSummary fields
FieldTypeDescription
assessmentIdrequiredstring (uuid)
titlerequiredstring
statusrequired"Active" | "Retired"
typerequiredAssessmentType
enrolledrequiredinteger

People enrolled now plus anyone with an attempt (enrolled earlier or launched directly).

completedrequiredinteger

People with at least one finished attempt.

completionRaterequirednumber

(0 to 1)

medianMinutesnumber | null
meetingTargetRaterequirednumber | null

Average over the skills of the share meeting the skill's target; null when suppressed. (0 to 1)

suppressedboolean

True for a manager's token whose reports finished fewer than the policy's minimumGroupSize.

AssessmentAnalytics

AssessmentAnalytics fields
FieldTypeDescription
assessmentIdrequiredstring (uuid)
titlerequiredstring
statusrequired"Active" | "Retired"
languagestring | null
enrolledrequiredinteger

People enrolled now plus anyone with an attempt (enrolled earlier or launched directly).

completedrequiredinteger

People with at least one finished attempt.

completionRaterequirednumber
medianMinutesnumber | null
skillsrequiredobject[]
teamsrequiredobject[]
groupBy"team" | "department" | "location" | "manager"

Present with groupBy.

groupsobject[]

Present with groupBy. A null group is people without that attribute.

suppressedboolean

True for a manager's token whose reports finished fewer than minimumGroupSize: only the counts are given.

fromstring (date-time) | null
tostring (date-time) | null
questionsrequiredobject[]
completionsByDayrequiredobject[]

AnswerRecord

AnswerRecord fields
FieldTypeDescription
answerIdrequiredstring

<resultId>:<questionId>; unique per answer. Upsert on it.

resultIdrequiredstring

The attempt; joins to /results.

assessmentIdrequiredstring (uuid)
assessmentTitlestring
learnerEmailstring (email)

Present unless people=anonymous.

personIdstring

Stable anonymous ID (C-…) per organization; present when people=anonymous. Deliberately not the personId of /people, so it can't be traced back.

teamstring | null
attemptStatus"IN_PROGRESS" | "SUBMITTED" | "TIME_EXPIRED"
languagestring
questionIdrequiredstring (uuid)
questionNumberrequiredinteger
kindrequired"knowledge" | "roleplay" | "project"
typerequiredstring
skillstring | null
skillIdstring
objectivestring | null
bloomstring | null
difficultyinteger | null

(1 to 10)

questionstring
answerstring | null

The answer in words.

correctAnswerstring | null

The expected answer in words; null for open answers, role plays and projects, and when the organization's policy shares answer keys only with clients holding analytics:answer-keys.

managerEmailstring (email) | null

Null when people=anonymous.

pointsnumber | null
scorenumber | null
resultrequired"Correct" | "Partly correct" | "Wrong" | "Not scored"
answeredAtstring (date-time) | null
startedDatestring (date-time)
completedDatestring (date-time) | null
lastUpdatedrequiredstring (date-time)

When the attempt last changed; use for incremental sync.

proctoringLevel"LOW" | "MEDIUM" | "HIGH" | null

The attempt's proctoring level once scored; null when not proctored, or when the policy requires proctoring:read and the client lacks it.

EnrollmentRequest

EnrollmentRequest fields
FieldTypeDescription
emailrequiredstring (email)
firstNamestring

(up to 100 characters)

lastNamestring

(up to 100 characters)

assessmentIdsrequiredstring (uuid)[]

(up to 100 items)

notifyboolean

Send Creatium's invitation email. Leave false when the platform notifies learners itself. (default false)

EnrollmentResponse

EnrollmentResponse fields
FieldTypeDescription
emailrequiredstring (email)
resultsrequiredobject[]

Enrollment

Enrollment fields
FieldTypeDescription
personIdstring (uuid) | null
learnerEmailrequiredstring (email)
assessmentIdrequiredstring (uuid)
enrolledAtrequiredstring (date-time)
source"API" | "INVITE" | "LAUNCH" | "ASSIGNMENT"

AssignmentSettings

AssignmentSettings fields
FieldTypeDescription
namestring

(up to 200 characters)

assessmentIdsstring (uuid)[]

In order. Live assessments only. (up to 50 items)

dueAtstring (date-time) | null

Shown on each person's home; a reminder goes out 48 hours before to anyone unfinished.

closeAtDueboolean

After dueAt nobody can start a new attempt. (default false)

orderedboolean

Each assessment opens once the ones before it are finished. (default false)

maxAttemptsinteger | null

Attempts allowed at each assessment; null for no limit. (1 to 20)

AssignmentRequest

AssignmentRequest fields
FieldTypeDescription
namerequiredstring

(up to 200 characters)

assessmentIdsrequiredstring (uuid)[]

In order. Live assessments only. (up to 50 items)

dueAtstring (date-time) | null

Shown on each person's home; a reminder goes out 48 hours before to anyone unfinished.

closeAtDueboolean

After dueAt nobody can start a new attempt. (default false)

orderedboolean

Each assessment opens once the ones before it are finished. (default false)

maxAttemptsinteger | null

Attempts allowed at each assessment; null for no limit. (1 to 20)

emailsstring (email)[]

(up to 1000 items)

teamsstring[]

(up to 50 items)

notifyboolean

(default false)

AssignmentUpdate

AssignmentUpdate fields
FieldTypeDescription
namestring

(up to 200 characters)

assessmentIdsstring (uuid)[]

In order. Live assessments only. (up to 50 items)

dueAtstring (date-time) | null

Shown on each person's home; a reminder goes out 48 hours before to anyone unfinished.

closeAtDueboolean

After dueAt nobody can start a new attempt. (default false)

orderedboolean

Each assessment opens once the ones before it are finished. (default false)

maxAttemptsinteger | null

Attempts allowed at each assessment; null for no limit. (1 to 20)

MembersRequest

MembersRequest fields
FieldTypeDescription
emailsstring (email)[]

(up to 1000 items)

teamsstring[]

(up to 50 items)

notifyboolean

(default false)

Assignment

Assignment fields
FieldTypeDescription
assignmentIdrequiredstring (uuid)
namerequiredstring
assessmentIdsrequiredstring (uuid)[]
dueAtrequiredstring (date-time) | null
closeAtDuerequiredboolean
orderedrequiredboolean
maxAttemptsrequiredinteger | null
membersrequiredobject
members.emailsrequiredstring (email)[]
members.teamsrequiredstring[]
createdAtrequiredstring (date-time)
updatedAtrequiredstring (date-time)
archivedAtstring (date-time) | null

Set once archived; only listed with includeArchived=true.

MemberOutcome

MemberOutcome fields
FieldTypeDescription
emailstring (email)
teamstring
statusrequired"added" | "already_member" | "failed"
errorobject
error.coderequired"email_outside_domains"
error.detailrequiredstring

ProgressStatus"NOT_STARTED" | "IN_PROGRESS" | "COMPLETED"

NOT_STARTED: no attempt at any assessment in it. IN_PROGRESS: at least one started, not all finished. COMPLETED: every assessment has a finished attempt.

AssignmentProgress

AssignmentProgress fields
FieldTypeDescription
personIdstring (uuid) | null
learnerEmailrequiredstring (email)
teamstring | null
managerEmailstring (email) | null
statusrequiredProgressStatus
passedrequiredboolean | null

Across the certifications in it only. Null when there are none, or any is unfinished or awaiting release.

overduerequiredboolean

Past dueAt and not completed.

completedAtrequiredstring (date-time) | null
assessmentsrequiredobject[]

AssignmentEvent

AssignmentEvent fields
FieldTypeDescription
idrequiredstring (uuid)
typerequired"assignment.completed"
createdAtrequiredstring (date-time)
datarequiredAssignmentProgress

Person

Person fields
FieldTypeDescription
personIdrequiredstring (uuid) | null

Stable; survives an email change.

emailrequiredstring (email)
teamrequiredstring | null
externalIdrequiredstring | null

The employer's own ID (employee number), unique in the organization. (up to 200 characters)

managerEmailstring (email) | null

Their manager (a reporting line).

departmentstring | null

(up to 100 characters)

locationstring | null

(up to 100 characters)

PersonUpdate

Send any of these; fields left out stay as they are, and null clears one.

PersonUpdate fields
FieldTypeDescription
teamstring | null

(up to 100 characters)

externalIdstring | null

(up to 200 characters)

managerEmailstring (email) | null

Not the person themselves.

departmentstring | null

(up to 100 characters)

locationstring | null

(up to 100 characters)

PeopleBatchRequest

PeopleBatchRequest fields
FieldTypeDescription
peoplerequiredPersonUpdate[]

(up to 1000 items)

PeopleBatchResponse

PeopleBatchResponse fields
FieldTypeDescription
resultsrequiredobject[]

PersonProfile

PersonProfile fields
FieldTypeDescription
personIdrequiredstring (uuid) | null

Stable; survives an email change.

emailrequiredstring (email)
teamrequiredstring | null
externalIdrequiredstring | null

The employer's own ID (employee number), unique in the organization. (up to 200 characters)

managerEmailstring (email) | null

Their manager (a reporting line).

departmentstring | null

(up to 100 characters)

locationstring | null

(up to 100 characters)

skillsrequiredobject[]

Latest released level per skill across every assessment, with the one before.

assignmentsrequiredobject[]
certificatesrequiredobject[]

Policy

Policy fields
FieldTypeDescription
managerAccessrequired"none" | "aggregates" | "individual"

What a token issued for a manager may read (default aggregates).

minimumGroupSizerequiredinteger

Analytics never break out a group with fewer finished attempts (default 5). (1 to 50)

proctoringRequiresScoperequiredboolean

True: proctoring only for clients holding proctoring:read.

answerKeysRequireScoperequiredboolean

True: correctAnswer only for clients holding analytics:answer-keys.

Event

Exactly the body a webhook endpoint receives.

Event fields
FieldTypeDescription
idrequiredstring (uuid)

Same as the Creatium-Event-Id header.

typerequired"result.updated" | "assignment.completed" | "enrollment.created" | "enrollment.removed" | "assignment.updated" | "assignment.archived" | "assessment.updated" | "person.erased"
createdAtrequiredstring (date-time)
datarequiredobject

Per type: Result, AssignmentProgress (+ assignmentId, name), EnrollmentEventData, AssignmentEventData, AssessmentEventData or {personId, erasedAt}.

EnrollmentEventData

EnrollmentEventData fields
FieldTypeDescription
personIdrequiredstring (uuid) | null
learnerEmailrequiredstring (email)
assessmentIdrequiredstring (uuid)
enrolledAtstring (date-time)

enrollment.created only.

source"API" | "INVITE" | "LAUNCH" | "ASSIGNMENT"

enrollment.created only.

removedAtstring (date-time)

enrollment.removed only.

resultIdstring | null

enrollment.removed only: the REMOVED record's resultId, or null when they had attempts (those stay).

AssignmentEventData

Read GET /assignments/{assignmentId} (or ?includeArchived=true) for the rest.

AssignmentEventData fields
FieldTypeDescription
assignmentIdrequiredstring (uuid)
namerequiredstring
updatedAtrequiredstring (date-time)
archivedAtrequiredstring (date-time) | null

AssessmentEventData

Read GET /assessments/{assessmentId} for the rest.

AssessmentEventData fields
FieldTypeDescription
assessmentIdrequiredstring (uuid)
statusrequired"Active" | "Retired"
lastUpdatedDaterequiredstring (date-time)

WebhookEndpoint

WebhookEndpoint fields
FieldTypeDescription
endpointIdrequiredstring (uuid)
urlrequiredstring (uri)

Public https URL.

eventsrequiredstring[]

Any of the Event types, and the older assessment.completed.

createdAtrequiredstring (date-time)
previousSecretExpiresAtrequiredstring (date-time) | null

Until when deliveries also carry a signature with the previous secret.

secretstring

The signing secret: only in the responses that create or rotate it.

WebhookEndpointRequest

WebhookEndpointRequest fields
FieldTypeDescription
urlrequiredstring (uri)
eventsrequiredstring[]

ClientSecret

ClientSecret fields
FieldTypeDescription
secretIdrequiredstring (uuid)
lastFourrequiredstring
createdAtrequiredstring (date-time)
clientSecretstring

The secret itself: only in the response that creates it.

Erasure

Erasure fields
FieldTypeDescription
personIdrequiredstring (uuid)
statusrequired"ERASED"
erasedAtrequiredstring (date-time)

ErasureEvent

ErasureEvent fields
FieldTypeDescription
idrequiredstring (uuid)
typerequired"person.erased"
createdAtrequiredstring (date-time)
datarequiredobject
data.personIdrequiredstring (uuid)
data.erasedAtrequiredstring (date-time)

BatchEnrollmentRequest

Up to 1,000 people and 10,000 (person, assessment) pairs in one call.

BatchEnrollmentRequest fields
FieldTypeDescription
enrollmentsrequiredobject[]

(up to 1000 items)

notifyboolean

Send Creatium's invitation email to each person newly enrolled. (default false)

BatchEnrollmentResponse

BatchEnrollmentResponse fields
FieldTypeDescription
resultsrequiredEnrollmentResponse[]

One per person

Question

Metadata only, never the question's text or answer. Field names match AnswerRecord, so they join on questionId.

Question fields
FieldTypeDescription
questionIdrequiredstring (uuid)
kindrequired"knowledge" | "roleplay" | "project"
typerequiredstring

single-choice, multiple-choice, ordering, role-play, project, …

skillstring | null
skillIdrequiredstring
objectivestring | null
bloom"Remember" | "Understand" | "Apply" | "Analyze" | "Evaluate" | "Create" | null
difficultyinteger | null

(1 to 10)

weightinteger

(1 to 5)

statusrequired"ACTIVE" | "RETIRED"

RETIRED questions are no longer served but may appear in older answers.

Problem

RFC 9457 problem details. code is stable and machine-readable; detail is for people.

Problem fields
FieldTypeDescription
typerequiredstring (uri)

The error code explained, at https://app.assess.creatium.com/docs#error-<code>.

titlerequiredstring
statusrequiredinteger
coderequired"invalid_request" | "invalid_token" | "insufficient_scope" | "not_found" | "method_not_allowed" | "assessment_not_found" | "assessment_not_active" | "idempotency_conflict" | "enrollment_from_assignment" | "email_in_use" | "external_id_in_use" | "secret_limit" | "precondition_failed" | "unprocessable" | "rate_limited" | "internal_error" | "service_unavailable"
detailstring
requestIdrequiredstring

Quote it to support. Also returned as the X-Request-Id header.

errorsobject[]

Field-level problems (400 and 422).