LTI 1.3 API Reference
LTI 1.3 Advantage integration for Creatiq. Enables single-click content launch from any LMS, deep linking for content selection, Assignment and Grade Services (AGS) for gradebook sync, and Names and Role Provisioning Services (NRPS) for roster retrieval.
Base path: /lti
Endpoints
POST /lti/launch
Handle an LMS launch request (OIDC callback with id_token).
| Property | Value |
|---|---|
| Auth | None (public -- token is self-authenticating) |
| Feature gate | None (checked via ltiEnabled config flag) |
Description
Receives the id_token JWT from the LMS after the OIDC login flow. Validates the token against registered platforms, checks nonce freshness, then redirects the user to the appropriate Creatiq workspace URL.
Redirect logic:
- If the token contains
target_link_uriclaim -- redirect there. - If the token contains
resource_link.id-- redirect to/workspace?lti=true&resource={id}. - Otherwise -- redirect to
/workspace?lti=true.
Request
Content-Type: application/x-www-form-urlencoded
| Field | Type | Required | Description |
|---|---|---|---|
id_token | string | Yes | JWT issued by the LMS platform |
Response
302 Redirect to workspace URL on success.
Errors
| Status | Error | Condition |
|---|---|---|
| 400 | Missing id_token parameter | id_token not present in request body |
| 401 | Unregistered platform | Token issuer/clientId not found in registered platforms |
| 401 | Invalid or expired nonce | Nonce already consumed or expired (600s TTL) |
| 401 | Invalid token format | JWT cannot be decoded |
| 503 | LTI integration is not configured | ltiEnabled is false (missing env vars) |
GET /lti/jwks
Public JWKS endpoint for LTI platforms to verify Creatiq's signatures.
| Property | Value |
|---|---|
| Auth | None (public) |
| Feature gate | None |
Description
Returns the JSON Web Key Set containing Creatiq's RSA-256 public key. Platforms use this to verify deep-link response JWTs. Keys are auto-generated on first use (2048-bit RSA) and persisted in the lti_keys table.
Response
{
"keys": [
{
"kty": "RSA",
"n": "...",
"e": "AQAB",
"kid": "uuid",
"use": "sig",
"alg": "RS256"
}
]
}
Errors
| Status | Error | Condition |
|---|---|---|
| 500 | Failed to get JWKS | Database or key generation failure |
GET /lti/config
Get LTI platform configuration URLs.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | lti-integration |
Description
Returns the URLs that an LMS administrator needs when registering Creatiq as an LTI tool.
Response
{
"success": true,
"data": {
"enabled": true,
"launchUrl": "https://app.creatiq.com/lti/launch",
"jwksUrl": "https://app.creatiq.com/lti/jwks",
"deepLinkUrl": "https://app.creatiq.com/lti/deep-link",
"loginUrl": "https://app.creatiq.com/lti/login"
}
}
Errors
| Status | Error | Condition |
|---|---|---|
| 401 | Unauthorized | Missing or invalid JWT |
| 403 | Feature not available | lti-integration feature not enabled for subscription tier |
POST /lti/platforms
Register a new LTI platform.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | lti-integration |
Description
Registers an LMS platform for LTI 1.3 integration. Uses upsert logic -- if a platform with the same issuer + clientId already exists, it updates the record.
Request
{
"issuer": "https://lms.example.com",
"clientId": "creatiq-tool-12345",
"name": "Example LMS",
"authLoginUrl": "https://lms.example.com/auth/login",
"authTokenUrl": "https://lms.example.com/auth/token",
"keysetUrl": "https://lms.example.com/.well-known/jwks.json",
"deploymentId": "deploy-001",
"agsEndpoint": "https://lms.example.com/api/ags",
"nrpsEndpoint": "https://lms.example.com/api/nrps"
}
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
issuer | string | Yes | URL, max 500 | Platform issuer URL |
clientId | string | Yes | 1--255 chars | OAuth 2.0 client ID |
name | string | Yes | 1--255 chars | Display name |
authLoginUrl | string | Yes | URL, max 500 | OIDC login initiation URL |
authTokenUrl | string | Yes | URL, max 500 | OAuth 2.0 token endpoint |
keysetUrl | string | Yes | URL, max 500 | Platform's JWKS URL |
deploymentId | string | No | Max 255 | LTI deployment ID |
agsEndpoint | string | No | URL, max 500 | AGS service base URL |
nrpsEndpoint | string | No | URL, max 500 | NRPS service base URL |
Response (201 Created)
{
"success": true,
"data": {
"id": "uuid",
"issuer": "https://lms.example.com",
"clientId": "creatiq-tool-12345",
"name": "Example LMS",
"authLoginUrl": "https://lms.example.com/auth/login",
"authTokenUrl": "https://lms.example.com/auth/token",
"keysetUrl": "https://lms.example.com/.well-known/jwks.json",
"deploymentId": "deploy-001",
"agsEndpoint": "https://lms.example.com/api/ags",
"nrpsEndpoint": "https://lms.example.com/api/nrps",
"createdAt": "2025-01-15T10:00:00.000Z",
"updatedAt": "2025-01-15T10:00:00.000Z"
}
}
Errors
| Status | Error | Condition |
|---|---|---|
| 400 | Validation error: ... | Zod schema validation failure |
| 401 | Unauthorized | Missing or invalid JWT |
| 403 | Feature not available | lti-integration feature not enabled |
POST /lti/deep-link
Create a deep linking response JWT for content selection.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | lti-integration |
Description
Generates a signed JWT that the LMS can use to register selected H5P content as LTI resource links. Each content item becomes an ltiResourceLink with a URL pointing to {appUrl}/h5p/play/{contentId}. The JWT is signed with RS256 using Creatiq's private key and expires in 5 minutes.
Request
{
"platformId": "uuid-of-registered-platform",
"contents": [
{
"type": "h5p",
"title": "Interactive Quiz: Biology Chapter 3",
"url": "https://app.creatiq.com/h5p/play/abc123",
"contentId": "abc123"
}
]
}
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
platformId | string | Yes | UUID | Registered platform ID |
contents | array | Yes | 1--50 items | Content items to link |
contents[].type | string | Yes | Non-empty | Content type identifier |
contents[].title | string | Yes | 1--500 chars | Display title in LMS |
contents[].url | string | Yes | URL | Content URL |
contents[].contentId | string | Yes | Non-empty | H5P content ID |
Response
{
"success": true,
"data": {
"jwt": "eyJhbGciOiJSUzI1NiIs..."
}
}
The JWT payload contains:
iss-- Creatiq app URLaud-- Platform issuerhttps://purl.imsglobal.org/spec/lti-dl/claim/content_items-- Array ofltiResourceLinkitemshttps://purl.imsglobal.org/spec/lti-dl/claim/msg--"Content selected successfully"
Errors
| Status | Error | Condition |
|---|---|---|
| 400 | Validation error: ... | Schema validation failure |
| 500 | Platform not found | platformId does not match a registered platform |
Assignment and Grade Services (AGS)
GET /lti/ags/lineitems
List line items (gradebook columns) for a platform context.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | lti-ags |
Description
Retrieves all line items from the LMS gradebook for a given context (course). Internally acquires an OAuth 2.0 access token from the platform using the lineitem scope.
Query Parameters
| Param | Type | Required | Description |
|---|---|---|---|
platformId | string | Yes (UUID) | Registered platform ID |
contextId | string | Yes (1--500) | LMS course/context ID |
Response
{
"success": true,
"data": [
{
"id": "https://lms.example.com/api/ags/lineitems/42",
"label": "H5P Quiz Score",
"scoreMaximum": 100,
"resourceId": "abc123",
"tag": "quiz",
"startDateTime": "2025-01-01T00:00:00Z",
"endDateTime": "2025-06-30T23:59:59Z"
}
]
}
Errors
| Status | Error | Condition |
|---|---|---|
| 400 | Validation error: ... | Invalid query params |
| 500 | AGS endpoint not configured for platform: {id} | Platform has no agsEndpoint |
| 500 | Failed to list line items: {status} | LMS returned an error |
POST /lti/ags/lineitems
Create a line item in the LMS gradebook.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | lti-ags |
Request
{
"platformId": "uuid",
"contextId": "course-101",
"label": "H5P Interactive Video Score",
"scoreMaximum": 100,
"resourceId": "content-abc123",
"tag": "h5p-assessment"
}
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
platformId | string | Yes | UUID | Registered platform ID |
contextId | string | Yes | 1--500 chars | LMS course/context ID |
label | string | Yes | 1--500 chars | Gradebook column name |
scoreMaximum | number | Yes | Positive | Maximum possible score |
resourceId | string | No | Max 500 | Associated resource ID |
tag | string | No | Max 255 | Category tag |
Response (201 Created)
{
"success": true,
"data": {
"id": "https://lms.example.com/api/ags/lineitems/43",
"label": "H5P Interactive Video Score",
"scoreMaximum": 100,
"resourceId": "content-abc123",
"tag": "h5p-assessment"
}
}
Errors
| Status | Error | Condition |
|---|---|---|
| 400 | Validation error: ... | Schema validation failure |
| 500 | AGS endpoint not configured for platform: {id} | No AGS endpoint |
| 500 | Failed to create line item: {status} | LMS returned an error |
POST /lti/ags/scores
Publish a score to a specific line item.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | lti-ags |
Description
Publishes a learner's score to an LMS gradebook column. The score is submitted with activityProgress: "Completed" and gradingProgress: "FullyGraded". Internally acquires an OAuth 2.0 token with the score scope.
Request
{
"platformId": "uuid",
"lineItemUrl": "https://lms.example.com/api/ags/lineitems/42",
"userId": "student-12345",
"scoreGiven": 85,
"scoreMaximum": 100,
"comment": "Great work on the interactive quiz!"
}
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
platformId | string | Yes | UUID | Registered platform ID |
lineItemUrl | string | Yes | URL, max 1000 | Full URL of the line item |
userId | string | Yes | 1--500 chars | LMS user identifier |
scoreGiven | number | Yes | >= 0 | Score achieved |
scoreMaximum | number | Yes | Positive | Maximum possible score |
comment | string | No | Max 1000 | Instructor comment |
Response
{
"success": true,
"message": "Score published"
}
Errors
| Status | Error | Condition |
|---|---|---|
| 400 | Validation error: ... | Schema validation failure |
| 500 | Failed to publish score: {status} | LMS returned an error |
| 500 | Token request failed with status {status} | OAuth token acquisition failed |
Names and Role Provisioning Services (NRPS)
GET /lti/nrps/members
Fetch the membership list (roster) from an LMS platform.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | lti-nrps |
Description
Retrieves all members of an LMS course context. Supports paginated responses -- follows Link header rel="next" up to 50 pages. LTI roles are mapped to simplified roles:
| LTI Role (contains) | Mapped Role |
|---|---|
Instructor, TeachingAssistant | instructor |
Administrator | admin |
Learner, Student | learner |
| (other) | other |
Query Parameters
| Param | Type | Required | Description |
|---|---|---|---|
platformId | string | Yes (UUID) | Registered platform ID |
contextId | string | Yes (1--500) | LMS course/context ID |
Response
{
"success": true,
"data": {
"id": "https://lms.example.com/api/nrps/memberships",
"context": {
"id": "course-101"
},
"members": [
{
"userId": "user-001",
"roles": ["instructor"],
"name": "Jane Doe",
"email": "jane@example.com",
"status": "Active"
},
{
"userId": "user-002",
"roles": ["learner"],
"name": "John Smith",
"email": "john@example.com",
"status": "Active"
}
]
}
}
Errors
| Status | Error | Condition |
|---|---|---|
| 400 | Validation error: ... | Invalid query params |
| 500 | NRPS endpoint not configured for platform: {id} | Platform has no nrpsEndpoint |
| 500 | Failed to fetch members: {status} | LMS returned an error |
OAuth 2.0 Token Flow
All AGS and NRPS requests require an OAuth 2.0 access token from the LMS. The token service handles this transparently:
- Check in-memory cache for a valid token (with 60-second buffer before expiry).
- If no cached token, create a
client_credentialsgrant request:client_assertion_type:urn:ietf:params:oauth:client-assertion-type:jwt-bearerclient_assertion: JWT signed with RS256 (iss/sub = clientId, aud = authTokenUrl, exp = 5 min)scope: Required LTI scopes for the operation
- Cache the returned token for its
expires_induration.
LTI Scopes Used
| Scope | Used By |
|---|---|
https://purl.imsglobal.org/spec/lti-ags/scope/lineitem | List/create line items |
https://purl.imsglobal.org/spec/lti-ags/scope/score | Publish scores |
https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly | Read results |
https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly | Fetch members |
Database Tables
lti_platforms
| Column | Type | Description |
|---|---|---|
id | UUID (PK) | Auto-generated |
issuer | VARCHAR(500) | Platform issuer URL |
client_id | VARCHAR(255) | OAuth client ID |
name | VARCHAR(255) | Display name |
auth_login_url | VARCHAR(500) | OIDC login URL |
auth_token_url | VARCHAR(500) | Token endpoint |
keyset_url | VARCHAR(500) | JWKS URL |
deployment_id | VARCHAR(255) | LTI deployment ID |
ags_endpoint | VARCHAR(500) | AGS service URL |
nrps_endpoint | VARCHAR(500) | NRPS service URL |
created_at | TIMESTAMP | Creation time |
updated_at | TIMESTAMP | Last update time |
Unique constraint: (issuer, client_id).
lti_keys
| Column | Type | Description |
|---|---|---|
kid | VARCHAR(64) (PK) | Key ID |
public_key | TEXT | PEM-encoded RSA public key |
private_key | TEXT | PEM-encoded RSA private key |
created_at | TIMESTAMP | Creation time |
lti_nonces
| Column | Type | Description |
|---|---|---|
value | VARCHAR(255) (PK) | Nonce string |
platform_id | UUID (FK) | References lti_platforms.id |
consumed_at | TIMESTAMP | When the nonce was used (null = unconsumed) |
expires_at | TIMESTAMP | Expiry time (600 seconds after creation) |
xAPI Integration
The AGS service listens for xAPI statements with score-trigger verbs (completed, passed, failed). When detected, these can trigger automatic grade passback to the LMS. This listener is initialized on service startup.