LTI 1.3 Integration Guide
This guide walks external system administrators through integrating their Learning Management System (LMS) with Creatiq using the LTI 1.3 Advantage standard.
1. Overview
Creatiq implements the IMS Global LTI 1.3 Advantage specification, enabling seamless integration with any LTI 1.3-compliant LMS. The integration supports:
- Resource Link Launch -- Single sign-on from the LMS directly into Creatiq H5P content.
- Deep Linking (LTI-DL) -- Instructors can browse and select Creatiq content from within the LMS course builder.
- Assignment and Grade Services (AGS) -- Scores from H5P interactions flow back to the LMS gradebook automatically.
- Names and Role Provisioning Services (NRPS) -- Course rosters sync from the LMS into Creatiq for enrollment management.
LTI integration is available on the Premium plan and requires the lti-integration feature flag to be enabled for your tenant.
2. Prerequisites
Before starting, confirm the following:
| Requirement | Detail |
|---|---|
| LMS version | Must support LTI 1.3 Advantage (e.g., Moodle 3.10+, Canvas, Blackboard Ultra, Brightspace) |
| Creatiq plan | Premium or Enterprise (feature flag: lti-integration) |
| Network access | The LMS must be able to reach the Creatiq server over HTTPS |
| Admin privileges | You need LMS administrator access to register an external tool |
| Environment variable | LTI_ENCRYPTION_KEY must be set on the Creatiq server |
Creatiq Environment Variables
The following environment variables must be configured on the Creatiq server:
| Variable | Required | Description |
|---|---|---|
LTI_ENCRYPTION_KEY | Yes | Encryption key for LTI security operations. Must be set for LTI to be enabled. |
LTI_PLATFORM_URL | No | Default LMS platform URL (for single-platform setups) |
LTI_PLATFORM_NAME | No | Human-readable name for the default platform |
LTI_CLIENT_ID | No | OAuth 2.0 client ID assigned by the default platform |
LTI_AUTH_ENDPOINT | No | Platform's OIDC authentication endpoint |
LTI_TOKEN_ENDPOINT | No | Platform's OAuth 2.0 token endpoint |
LTI_AUTH_SERVER | No | Authorization server URL (if different from token endpoint) |
NEXT_PUBLIC_APP_URL | Yes | Public URL of the Creatiq application (e.g., https://creatiq.example.com) |
If LTI_ENCRYPTION_KEY is not set, all LTI features are automatically disabled and the /lti/launch endpoint returns 503 Service Unavailable.
3. Platform Registration
Step 1: Retrieve Creatiq Tool Configuration
Call the configuration endpoint to get the URLs your LMS needs:
GET /lti/config
Authorization: Bearer <admin-token>
Response:
{
"success": true,
"data": {
"enabled": true,
"launchUrl": "https://creatiq.example.com/lti/launch",
"jwksUrl": "https://creatiq.example.com/lti/jwks",
"deepLinkUrl": "https://creatiq.example.com/lti/deep-link",
"loginUrl": "https://creatiq.example.com/lti/login"
}
}
Step 2: Register Creatiq as an External Tool in the LMS
In your LMS admin panel, create a new LTI 1.3 tool with the following values:
| LMS Field | Value |
|---|---|
| Tool URL / Launch URL | https://creatiq.example.com/lti/launch |
| Login Initiation URL | https://creatiq.example.com/lti/login |
| Public Keyset URL (JWKS) | https://creatiq.example.com/lti/jwks |
| Redirect URI | https://creatiq.example.com/lti/launch |
| Deep Linking URL | https://creatiq.example.com/lti/deep-link |
After saving, the LMS will provide you with:
- Issuer URL (e.g.,
https://lms.example.com) - Client ID
- OIDC Authentication URL
- OAuth 2.0 Token URL
- Platform JWKS URL
- Deployment ID (optional)
Step 3: Register the LMS Platform in Creatiq
Send these values to the Creatiq platform registration endpoint:
POST /lti/platforms
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"issuer": "https://lms.example.com",
"clientId": "abc-123-client-id",
"name": "My LMS",
"authLoginUrl": "https://lms.example.com/mod/lti/auth.php",
"authTokenUrl": "https://lms.example.com/mod/lti/token.php",
"keysetUrl": "https://lms.example.com/mod/lti/certs.php",
"deploymentId": "1",
"agsEndpoint": "https://lms.example.com/mod/lti/service.php",
"nrpsEndpoint": "https://lms.example.com/mod/lti/service.php"
}
All fields are validated with the following constraints:
| Field | Type | Constraints |
|---|---|---|
issuer | string | Valid URL, max 500 characters |
clientId | string | 1-255 characters |
name | string | 1-255 characters |
authLoginUrl | string | Valid URL, max 500 characters |
authTokenUrl | string | Valid URL, max 500 characters |
keysetUrl | string | Valid URL, max 500 characters |
deploymentId | string | Optional, max 255 characters |
agsEndpoint | string | Optional, valid URL, max 500 characters |
nrpsEndpoint | string | Optional, valid URL, max 500 characters |
A successful response returns the registered platform with its generated UUID:
{
"success": true,
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"issuer": "https://lms.example.com",
"clientId": "abc-123-client-id",
"name": "My LMS",
"authLoginUrl": "https://lms.example.com/mod/lti/auth.php",
"authTokenUrl": "https://lms.example.com/mod/lti/token.php",
"keysetUrl": "https://lms.example.com/mod/lti/certs.php",
"deploymentId": "1",
"agsEndpoint": "https://lms.example.com/mod/lti/service.php",
"nrpsEndpoint": "https://lms.example.com/mod/lti/service.php",
"createdAt": "2026-03-25T10:00:00.000Z",
"updatedAt": "2026-03-25T10:00:00.000Z"
}
}
If a platform with the same issuer + clientId already exists, the registration is updated (upsert behavior).
4. Launch Flow
The LTI 1.3 launch follows the IMS Security Framework (OIDC third-party login):
LMS Creatiq Browser
| | |
| 1. User clicks link | |
|----------------------------->| |
| 2. OIDC login initiation | |
| POST /lti/login | |
|----------------------------->| |
| 3. Redirect to LMS auth | |
|<-----------------------------| |
| 4. LMS authenticates user | |
| 5. POST /lti/launch | |
| (with id_token JWT) | |
|----------------------------->| |
| | 6. Validate id_token |
| | - Decode JWT |
| | - Match issuer+clientId |
| | - Validate nonce |
| | 7. Redirect user |
| |------- 302 redirect ------->|
| | |
Validation Steps (Step 6)
- The
id_tokenJWT is decoded (not yet signature-verified against the platform JWKS -- see note below). - The
iss(issuer) andaud(client ID) claims are matched against registered platforms. - If a
nonceclaim is present, it is validated and consumed (single-use, 10-minute expiry). - If the platform is not registered, the launch is rejected with
401 Unregistered platform.
Redirect Logic (Step 7)
After successful validation, the user is redirected based on claims in the token:
| Priority | Claim | Redirect Target |
|---|---|---|
| 1 | target_link_uri | Directly to the specified URL |
| 2 | resource_link.id | /workspace?lti=true&resource=<resourceId> |
| 3 | (none) | /workspace?lti=true |
Launch Token Claims
The LMS sends a JWT id_token containing standard LTI 1.3 claims:
| Claim | Description |
|---|---|
iss | Issuer (LMS platform URL) |
sub | Subject (user ID in the LMS) |
aud | Audience (Creatiq client ID) |
nonce | One-time nonce for replay protection |
https://purl.imsglobal.org/spec/lti/claim/message_type | LtiResourceLinkRequest |
https://purl.imsglobal.org/spec/lti/claim/version | 1.3.0 |
https://purl.imsglobal.org/spec/lti/claim/roles | User roles array |
https://purl.imsglobal.org/spec/lti/claim/target_link_uri | Target content URL |
https://purl.imsglobal.org/spec/lti/claim/resource_link | Resource link object (id, title) |
https://purl.imsglobal.org/spec/lti/claim/context | Course context (id, label, title) |
5. Deep Linking
Deep Linking allows instructors to select Creatiq H5P content from within the LMS course editor and embed it as a link.
Flow
- The LMS sends a
LtiDeepLinkingRequestlaunch to Creatiq. - Creatiq presents a content picker to the instructor.
- The instructor selects one or more H5P content items.
- Creatiq creates a signed Deep Linking Response JWT and returns it to the LMS.
Creating a Deep Link Response
POST /lti/deep-link
Authorization: Bearer <user-token>
Content-Type: application/json
{
"platformId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"contents": [
{
"type": "ltiResourceLink",
"title": "Cell Biology Quiz",
"url": "https://creatiq.example.com/h5p/play/content-123",
"contentId": "content-123"
}
]
}
Validation constraints:
| Field | Constraints |
|---|---|
platformId | Valid UUID |
contents | Array of 1-50 items |
contents[].type | Non-empty string |
contents[].title | 1-500 characters |
contents[].url | Valid URL |
contents[].contentId | Non-empty string |
The response contains a signed JWT:
{
"success": true,
"data": {
"jwt": "eyJhbGciOiJSUzI1NiIsInR5cCI6..."
}
}
JWT Payload Structure
The Deep Linking Response JWT contains:
{
"iss": "https://creatiq.example.com",
"aud": "https://lms.example.com",
"nonce": "<random-uuid>",
"https://purl.imsglobal.org/spec/lti-dl/claim/content_items": [
{
"type": "ltiResourceLink",
"title": "Cell Biology Quiz",
"url": "https://creatiq.example.com/h5p/play/content-123",
"custom": {
"contentId": "content-123"
}
}
],
"https://purl.imsglobal.org/spec/lti-dl/claim/msg": "Content selected successfully"
}
The JWT is signed with RS256 using Creatiq's RSA key pair (2048-bit). The LMS verifies it against the Creatiq JWKS endpoint (/lti/jwks). The token expires after 5 minutes.
6. Grade Passback (AGS)
Assignment and Grade Services allow Creatiq to push H5P content scores back to the LMS gradebook. This requires the lti-ags feature flag.
Prerequisites
- The registered platform must have an
agsEndpointconfigured. - The LMS must grant the following OAuth 2.0 scopes:
https://purl.imsglobal.org/spec/lti-ags/scope/lineitem(read/write line items)https://purl.imsglobal.org/spec/lti-ags/scope/score(write scores)https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly(read results)
Authentication
AGS requests use the OAuth 2.0 Client Credentials flow:
- Creatiq creates a JWT client assertion signed with its RSA private key.
- The assertion is sent to the platform's token endpoint.
- The platform returns an access token (cached with a 60-second expiry buffer).
Line Items
A line item represents a column in the LMS gradebook.
List line items:
GET /lti/ags/lineitems?platformId=<uuid>&contextId=<course-id>
Authorization: Bearer <admin-token>
Create a line item:
POST /lti/ags/lineitems
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"platformId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"contextId": "course-101",
"label": "Cell Biology Quiz",
"scoreMaximum": 100,
"resourceId": "content-123",
"tag": "quiz"
}
Publishing Scores
To send a score to the LMS gradebook:
POST /lti/ags/scores
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"platformId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"lineItemUrl": "https://lms.example.com/mod/lti/service.php/lineitems/42",
"userId": "student-456",
"scoreGiven": 85,
"scoreMaximum": 100,
"comment": "Good work on the quiz!"
}
Creatiq automatically sets activityProgress to Completed and gradingProgress to FullyGraded when publishing scores.
Automatic Score Triggers
Creatiq listens for xAPI statements with the following verbs and can trigger automatic AGS score publishing:
http://adlnet.gov/expapi/verbs/completedhttp://adlnet.gov/expapi/verbs/passedhttp://adlnet.gov/expapi/verbs/failed
Score Object Structure
| Field | Type | Description |
|---|---|---|
userId | string | Learner identifier in the LMS |
scoreGiven | number | The score earned (min: 0) |
scoreMaximum | number | Maximum possible score (positive) |
comment | string | Optional comment (max 1000 chars) |
activityProgress | enum | Initialized, Started, InProgress, Submitted, Completed |
gradingProgress | enum | FullyGraded, Pending, PendingManual, Failed, NotReady |
timestamp | ISO 8601 | Time of the score submission |
7. Roster Sync (NRPS)
Names and Role Provisioning Services allow Creatiq to retrieve the course membership list from the LMS. This requires the lti-nrps feature flag.
Prerequisites
- The registered platform must have an
nrpsEndpointconfigured. - The LMS must grant the scope:
https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly
Fetching Members
GET /lti/nrps/members?platformId=<uuid>&contextId=<course-id>
Authorization: Bearer <admin-token>
Response:
{
"success": true,
"data": {
"id": "https://lms.example.com/mod/lti/service.php/memberships",
"context": {
"id": "course-101"
},
"members": [
{
"userId": "user-1",
"roles": ["instructor"],
"name": "Jane Smith",
"email": "jane@example.com",
"status": "Active"
},
{
"userId": "user-2",
"roles": ["learner"],
"name": "John Doe",
"email": "john@example.com",
"status": "Active"
}
]
}
}
Role Mapping
LTI roles from the LMS are mapped to Creatiq roles as follows:
| LTI Role (contains) | Creatiq Role |
|---|---|
Instructor, TeachingAssistant | instructor |
Administrator | admin |
Learner, Student | learner |
| (other) | other |
Pagination
NRPS supports paginated responses. Creatiq automatically follows Link header rel="next" URLs to retrieve all members, up to a maximum of 50 pages per request.
8. Endpoint Reference
All LTI endpoints are mounted under the /lti prefix.
Public Endpoints (No Auth)
| Method | Path | Description |
|---|---|---|
POST | /lti/launch | OIDC launch callback (receives id_token) |
GET | /lti/jwks | Public JWKS endpoint for signature verification |
Authenticated Endpoints (Premium Feature)
| Method | Path | Feature Flag | Description |
|---|---|---|---|
GET | /lti/config | lti-integration | Get tool configuration URLs |
POST | /lti/platforms | lti-integration | Register an LMS platform |
POST | /lti/deep-link | lti-integration | Create deep linking response JWT |
GET | /lti/ags/lineitems | lti-ags | List gradebook line items |
POST | /lti/ags/lineitems | lti-ags | Create a gradebook line item |
POST | /lti/ags/scores | lti-ags | Publish a score to the gradebook |
GET | /lti/nrps/members | lti-nrps | Fetch course membership roster |
JWKS Endpoint
The /lti/jwks endpoint returns the public key in JWK format, used by the LMS to verify JWTs signed by Creatiq:
{
"keys": [
{
"kty": "RSA",
"n": "...",
"e": "AQAB",
"kid": "uuid-key-id",
"use": "sig",
"alg": "RS256"
}
]
}
Creatiq auto-generates a 2048-bit RSA key pair on first use and persists it in the lti_keys database table.
9. Troubleshooting
Common Issues
| Symptom | Cause | Solution |
|---|---|---|
/lti/launch returns 503 | LTI_ENCRYPTION_KEY not set | Set the environment variable and restart the server |
| Launch returns "Unregistered platform" | Issuer/Client ID mismatch | Verify the issuer and clientId match the LMS exactly |
| "Invalid or expired nonce" | Launch took longer than 10 minutes, or nonce was replayed | Retry the launch from the LMS |
| AGS returns "AGS endpoint not configured" | Platform was registered without agsEndpoint | Re-register the platform with the correct endpoint |
| NRPS returns "NRPS endpoint not configured" | Platform was registered without nrpsEndpoint | Re-register the platform with the correct endpoint |
| Token request failed (AGS/NRPS) | Platform rejected the client assertion | Verify the LMS has granted the required OAuth scopes |
| Deep link response rejected by LMS | Key mismatch or token expired | Ensure the LMS is fetching keys from /lti/jwks; deep link tokens expire after 5 minutes |
| Feature not available (403) | Feature flag not enabled | Enable lti-integration, lti-ags, or lti-nrps for your tenant |
Verifying the Integration
- Check LTI status:
GET /lti/configshould return"enabled": true. - Check JWKS:
GET /lti/jwksshould return a valid JWK withalg: RS256. - Test launch: Initiate a launch from the LMS and confirm you are redirected to Creatiq.
- Test AGS: Create a line item and publish a test score; verify it appears in the LMS gradebook.
- Test NRPS: Fetch members and verify the roster matches the LMS course enrollment.
Database Tables
The LTI integration uses three database tables (auto-created on startup):
| Table | Purpose |
|---|---|
lti_platforms | Registered LMS platforms (issuer, client ID, endpoints) |
lti_keys | RSA key pairs for JWT signing |
lti_nonces | One-time nonces for launch replay protection |
Expired nonces are cleaned up automatically via the cleanupExpiredNonces() service method.