After this guide an agent, or the developer who runs it, can register an LMS with TutorFlow so that Canvas, Moodle, Blackboard, or any LTI 1.3 LMS can launch TutorFlow content. Scores are not sent back to the LMS gradebook for content an agent makes through the API; see Grade passback.
The registration call is an API call an agent can make. The LMS settings in step 2 are made by an LMS administrator in the LMS, not through TutorFlow.
Overview
LTI (Learning Tools Interoperability) 1.3 is the industry standard for securely connecting external tools to a Learning Management System (LMS). It replaces older LTI versions with modern OAuth 2.0-based authentication, signed JWTs, and a well-defined launch flow.
TutorFlow acts as an LTI Tool (also called a Tool Provider). Once a registration exists, an LMS user opens TutorFlow content from an assignment in the LMS, signed in through the LMS, with no separate TutorFlow sign-in. No API route queues a grade passback to the LMS now; see Grade passback.
Prerequisites
Before setting up LTI, you need:
- A TutorFlow agent account. See Quick Start.
- Administrator access to your LMS (Canvas, Moodle, Blackboard, or any LTI 1.3-compatible platform).
- A key to create the LTI registration. The registration routes accept the live Content key of your agent account (
tf_content_, like the one registration and the dashboard issue) and a legacytf_platform_key. A test key gets403platform_content_key_too_narrow. See Authentication.
Step-by-Step Setup
Step 1: Create an LTI Registration
Call the TutorFlow API to register your LMS as an LTI platform. You will need the issuer URL, authorization endpoint, token endpoint, and JWKS URL from your LMS.
curl -X POST https://api.tutorflow.io/v1/platform/lti/registrations \
-H "Authorization: Bearer tf_content_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Canvas Production",
"issuer": "https://canvas.instructure.com",
"clientId": "10000000000001",
"authorizationEndpoint": "https://canvas.instructure.com/api/lti/authorize_redirect",
"tokenEndpoint": "https://canvas.instructure.com/login/oauth2/token",
"jwksEndpoint": "https://canvas.instructure.com/api/lti/security/jwks"
}'201 Created with the registration. platformWorkspaceId is the workspace id, the same id the account routes call organizationId:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"platformWorkspaceId": "workspace-uuid",
"name": "Canvas Production",
"issuer": "https://canvas.instructure.com",
"clientId": "10000000000001",
"authorizationEndpoint": "https://canvas.instructure.com/api/lti/authorize_redirect",
"tokenEndpoint": "https://canvas.instructure.com/login/oauth2/token",
"jwksEndpoint": "https://canvas.instructure.com/api/lti/security/jwks",
"status": "ACTIVE",
"metadata": null,
"createdAt": "2026-03-25T12:00:00Z",
"updatedAt": "2026-03-25T12:00:00Z",
"loginInitiationPath": "/v1/platform/lti/login/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}loginInitiationPath is this registration's own OIDC login URL path. Prefix it with https://api.tutorflow.io and give it to the LMS as the login URL.
Errors: 401 platform_invalid_api_key for a missing or unknown key, 403 platform_content_key_too_narrow for a test key, and 429 platform_rate_limit_exceeded (wait Retry-After seconds) over the account's rate limit.
Configure TutorFlow as an external tool in your LMS with these URLs:
- OIDC Login Initiation:
https://api.tutorflow.io/v1/platform/lti/login/{registrationId}, the registration's own URL (loginInitiationPath) - Launch/Redirect:
https://api.tutorflow.io/v1/platform/lti/launch - Public JWKS:
https://api.tutorflow.io/v1/platform/lti/.well-known/jwks.json
Step 2: Configure Your LMS
Enter the URLs above in your LMS to configure TutorFlow as an external tool.
Use the registration's own login URL whenever the registration exists. A launch
through it resolves to that registration only, so a registration with the same
issuer and client ID in another account cannot block it. Some LMSs ask for a login
URL before they show the client ID, and the registration needs that client ID. In
that case enter the generic https://api.tutorflow.io/v1/platform/lti/login first,
create the registration, then replace it with the registration's own URL. The
generic URL works while the issuer and client ID match exactly one registration;
if more than one matches, it refuses the launch and its error points to the
per-registration URL.
Canvas
- Go to Admin > Developer Keys > + Developer Key > LTI Key.
- Enter the following values:
| LMS Field | Value |
|---|---|
| Redirect URIs | https://api.tutorflow.io/v1/platform/lti/launch |
| Target Link URI | https://api.tutorflow.io/v1/platform/lti/launch |
| OpenID Connect Initiation URL | https://api.tutorflow.io/v1/platform/lti/login/{registrationId} |
| Public JWK URL | https://api.tutorflow.io/v1/platform/lti/.well-known/jwks.json |
- Enable the developer key and add TutorFlow as an external tool in your course settings.
Moodle
- Go to Site Administration > Plugins > Activity Modules > External Tool > Manage Tools.
- Click Configure a tool manually and enter:
| Moodle Field | Value |
|---|---|
| Tool URL | https://api.tutorflow.io/v1/platform/lti/launch |
| LTI version | LTI 1.3 |
| Initiate login URL | https://api.tutorflow.io/v1/platform/lti/login/{registrationId} |
| Redirection URI(s) | https://api.tutorflow.io/v1/platform/lti/launch |
| Public keyset URL | https://api.tutorflow.io/v1/platform/lti/.well-known/jwks.json |
- Copy the platform ID, client ID, and deployment ID from Moodle back into your TutorFlow registration using the Update Registration endpoint.
Blackboard
- Go to System Admin > LTI Tool Providers > Register LTI 1.3 Tool.
- Enter the client ID from TutorFlow, then fill in:
| Blackboard Field | Value |
|---|---|
| Tool Provider URL | https://api.tutorflow.io/v1/platform/lti/launch |
| Login Initiation URL | https://api.tutorflow.io/v1/platform/lti/login/{registrationId} |
| Tool Provider JWKS URL | https://api.tutorflow.io/v1/platform/lti/.well-known/jwks.json |
Step 3: Test the Launch Flow
- Create an assignment in your LMS that uses TutorFlow as the external tool.
- Open the assignment as a test student.
- Verify that the OIDC login redirect completes and TutorFlow loads inside the LMS iframe.
- Confirm the launch redirects to
/platform/lti/launch?sessionId={sessionId}and opens the linked content.
OIDC Launch Flow
The LTI 1.3 launch follows the OpenID Connect (OIDC) third-party login flow. Here is how it works step by step:
LMS user opens the assignment
|
v
LMS redirects to TutorFlow Login URL
GET /v1/platform/lti/login/{registrationId}
(iss, login_hint, target_link_uri, lti_message_hint, client_id)
|
v
TutorFlow validates the request and redirects
back to the LMS authorization endpoint
(state, nonce, redirect_uri, client_id)
|
v
LMS authenticates the user and creates a signed JWT (id_token)
|
v
LMS POSTs to TutorFlow Launch URL
POST /v1/platform/lti/launch
(id_token, state)
|
v
TutorFlow validates the JWT signature using the LMS JWKS
|
v
TutorFlow creates an LTI session and redirects to the launch page
/platform/lti/launch?sessionId={sessionId}All tokens are signed JWTs verified against the LMS public key set. The state parameter
prevents CSRF attacks. TutorFlow never receives the user's LMS password.
Grade Passback (AGS)
The Assignment and Grade Services (AGS) specification lets a tool post scores to the LMS gradebook. TutorFlow stores the AGS claim of each launch, but no API route queues a grade passback now:
- Evaluations made on the Content API (
POST /v1/content/classrooms/{classroomId}/evaluations) storeclientMetadata, including anltiLaunchSessionIdyou put there, for your own correlation. They do not send a score to the LMS. - The route that did,
POST /v1/platform/evaluationswithclientMetadata.ltiLaunchSessionId, was removed on 2026-10-16 and answers410platform_route_removed. - Tests made on the removed
/v1/platform/testsroutes used to send their score when taken on the Platform test page. That page was removed on 2026-10-16; launching such a test opens its classroom page, and no score is sent.
If your integration needs scores in the LMS gradebook, post them from your own LTI tool, or
read them from TutorFlow (GET /v1/content/classrooms/{classroomId}/evaluations) and enter them
in the LMS. The score format and the retry rules of the grade-sync job are in
LTI Grade Sync.
Deep Linking
Deep Linking (LTI-DL) lets the LMS user who builds an assignment (an LTI Instructor role)
pick one TutorFlow resource for it, instead of a generic launch.
When the LMS sends a deep linking request, TutorFlow shows a picker. The LMS user picks the content, and TutorFlow returns a signed deep link response that the LMS saves with the assignment. Later launches of that assignment open that content directly.
A registration created with an API key belongs to the agent workspace. Its deep linking
picker lists the content the workspace made on the old /v1/platform content routes before
they were removed on 2026-10-16, not the classroom content made through the Content API.
Launching one of those items now opens its classroom page, and test scores are no longer
sent back to the LMS for them. A registration that an organization admin
creates while signed in to TutorFlow lists the organization's classroom content: courses,
modules, public slide decks, and tests.
Troubleshooting
"Invalid issuer" error during launch
The issuer in your TutorFlow registration must exactly match the iss claim
in the JWT sent by your LMS. Common issues:
- Trailing slash mismatch (
https://canvas.instructure.comvshttps://canvas.instructure.com/) - Using the wrong URL (e.g., your institution-specific URL vs the global Canvas URL)
"JWKS validation failed" error
- Verify the
jwksEndpointis correct and publicly accessible. - Check that the JWKS endpoint returns keys in the expected JWK Set format.
- Ensure the key used to sign the LMS JWT is included in the JWKS response.
Grade not appearing in LMS
This is expected for content an agent makes through the API: no API route queues a grade passback. See Grade passback.
"State mismatch" error
This usually means the LMS user's browser blocked third-party cookies. LTI 1.3 launches happen inside iframes, and some browsers block cookies in that context. Solutions:
- Ensure your LMS domain is trusted in the browser.
- Use the LMS in a non-incognito window.
- Check that TutorFlow cookies use
SameSite=None; Secureattributes.
Launch works but iframe is blank
- Check the browser console for Content Security Policy (CSP) errors.
- Ensure the LMS allows framing TutorFlow's domain.
- Verify that TutorFlow sets the
Content-Security-Policy: frame-ancestorsheader to include your LMS domain.