Canvas LTI Integration: A Practical Guide for Admins and Developers
A two-track walkthrough of Canvas LTI 1.3 and LTI Advantage: the exact Developer Key fields an admin fills in, the OIDC/JWT endpoints a developer builds, how AGS returns grades to the gradebook, and the launch errors that break real deployments.

Table of Contents
Quick navigation within the article
- Introduction
- What LTI is and why LTI 1.3 and Advantage matter for Canvas
- How to configure a Developer Key and deploy your LTI tool in Canvas
- Creating the Developer Key
- How to build an LTI 1.3 Advantage tool that works with Canvas
- Minimal pseudocode: validating the id_token
- Deep Linking response (Content-Item)
- How grade return (AGS) and other LTI Advantage services map to Canvas
- Service overview
- Posting a score via AGS
- Testing checklist and common launch/grade errors
- Pre-launch checklist
- Common errors and fixes
- Security and student-data privacy considerations for Canvas LTI integrations
- Practical examples: config URL flow and an Assignify grading tool walkthrough
- What a provider's configuration URL/JSON contains
- Assignify in a Canvas LTI workflow
- One-page quick checklist for admins and developers
- Administrator checklist
- Developer checklist
- Key Takeaways
- Why the LTI rollout conversation is missing a key step
- Assignify brings LTI-ready AI grading directly into your Canvas gradebook
- Authoritative sources for Canvas and LTI developer documentation
- FAQ
Canvas fully supports LTI 1.3 and LTI Advantage, the current standard for secure, grade-aware tool launches, and getting a working integration requires two parallel tracks: an administrator creating and enabling a Developer Key in Canvas, and a developer building the OIDC/JWT endpoints on the tool side. Before you touch a single Canvas menu, run through this starting checklist.
Admin first steps:
- Confirm which Canvas environment you are working in: production, beta (
beta.instructure.com), or test (test.instructure.com). Always start in beta or test. - Collect from the tool provider: OIDC initiation URL, redirect URI(s), JWKS URL, the exact list of scopes the tool requests, the privacy level it needs, and either a configuration JSON/URL or the manual parameters.
- Decide which LTI Advantage services you need: Assignment and Grade Services (AGS), Names and Roles Provisioning Service (NRPS), Deep Linking, and Platform Notification Service (PNS).
Developer first steps:
- Expose an OIDC initiation endpoint that accepts Canvas's login request and returns a browser redirect (not a server-to-server call).
- Host a JWKS endpoint so Canvas can fetch your public keys for JWT verification.
- Register one or more redirect URIs that exactly match what you will give the admin for the Developer Key.
- Implement handlers for whichever Advantage services the integration requires (AGS score POST, NRPS roster fetch, Deep Linking response).
The role split is deliberate. Admins control what Canvas trusts; developers control what the tool does with that trust. Both sides must be in sync before a single launch succeeds.
What LTI is and why LTI 1.3 and Advantage matter for Canvas
Learning Tools Interoperability (LTI) is the 1EdTech standard that lets Canvas securely pass user identity, course context, and grade data to an external tool, and receive results back, without requiring students or instructors to log in twice. Think of it as a structured handshake: Canvas vouches for the user, the tool receives a signed token, and both sides agree on what data can flow between them.
LTI 1.1 used OAuth 1.0 signatures and XML-based messages. It still works in many legacy deployments, but it carries real limitations: no standardized grade-return API, no roster service, and a weaker security model that relies on shared secrets rather than asymmetric keys. LTI 1.3 replaces that with an OIDC-based launch flow and JWT payloads, which means Canvas signs every launch message with a private key and the tool verifies it against Canvas's published JWKS. That shift matters practically: you get cryptographic proof of who initiated the launch, not just a shared secret that could be replicated.
LTI Advantage layers three core services on top of LTI 1.3: AGS for grade return, NRPS for roster access, and Deep Linking for content selection. PNS adds asynchronous server-to-server notifications. Together, they cover the full lifecycle of a tool embedded in a course, from content selection through grading through roster sync.
Canvas-specific behavior: The OIDC login initiation step must be a browser redirect, not a server-to-server HTTP call. Canvas validates the end-user's session as part of the flow. Tools that attempt a server-side OIDC request will see launch failures that can be difficult to diagnose because the error surfaces in the browser, not in server logs.
Pro Tip: If you are migrating a tool from LTI 1.1 to LTI 1.3, do not assume the same placement configuration carries over. LTI 1.3 Developer Keys use a separate configuration path in Canvas, and you will need to re-register the tool and update any existing course-level placements.
How to configure a Developer Key and deploy your LTI tool in Canvas
This is the admin's core task. The Developer Key configuration in Canvas is where the trust relationship between Canvas and the tool is established. You need account-level admin permissions to complete these steps.
Creating the Developer Key
- Log into Canvas as an account admin and navigate to Admin > [Account Name] > Developer Keys.
- Click + Developer Key and select LTI Key from the dropdown.
- Choose your configuration method: Manual Entry (fill in each field individually) or Enter URL / Paste JSON (auto-imports values from the tool provider's configuration document). Newer Canvas releases also offer Dynamic Registration, where you paste the tool's registration URL and Canvas negotiates the whole configuration with the tool directly. Any of the automated paths beats manual entry: they reduce transcription errors, pull in placement definitions, and carry settings that manual entry silently leaves at a default.
- Fill in or confirm the following fields:
- OIDC Initiation URL: the tool's endpoint that starts the login flow.
- Redirect URIs: one or more URIs where Canvas will POST the
id_token. Canvas matches everything up to the query string exactly, so a trailing slash difference will break the launch, while query parameters the tool appends to its launch URL are ignored by the comparison. - JWK Method: select Public JWK URL and enter the tool's JWKS endpoint, or paste a static JWK if the tool does not host a rotating key set.
- Domain: treat this as load-bearing rather than optional. Canvas matches a tool to a URL by host and port, so a tool served on a non-standard port must have that port in the domain, or Canvas cannot resolve the tool from links it stores.
- Tool ID: optional, and useful for placement filtering and analytics. Keep it stable, since anything Canvas keys on it is orphaned if it changes.
- Privacy Level: covered below, and the setting most often missed.
- Under LTI Advantage Services, enable the scopes the tool's documentation lists. Each toggle grants one specific scope:
- Can create and view assignment data in the gradebook (AGS LineItems, read and write)
- Can view assignment data in the gradebook (AGS LineItems, read-only)
- Can view submission data for assignments (AGS Results, read-only)
- Can create and update submission results (AGS Scores)
- Can retrieve user data associated with the context (NRPS)
- Can update public JWK (key rotation)
- Save the key. Canvas generates a Client ID, share this with the tool developer. It is the identifier the tool uses in its OIDC auth request.
- Toggle the key from Off to On in the Developer Keys list.
Two settings on this screen cause most of the "the tool installed fine but does not work" tickets.
Grant the tool's scope list exactly. Scopes are not graded permissions that a tool degrades around. The tool asks Canvas's token endpoint for the set it was built to request, and Canvas refuses the entire token request if the key does not carry all of them, answering with a 400 that names no scope at all. Trimming one read-only scope therefore breaks every AGS and NRPS call the tool makes, and the symptom points at the key rather than at the toggle. The place to minimize data access is the conversation with the vendor about which services the integration needs, not the toggles after they have told you.
Set the privacy level deliberately. Canvas decides how much it will tell the tool about a person from the key's privacy level: public, name_only, email_only, or anonymous. At anything below public, launches and NRPS roster responses arrive with an opaque user ID and no name or email. Manual entry defaults to the most restrictive value, a pasted configuration can carry privacy_level inside its Canvas extension, and dynamic registration infers a level from the claims the tool asks for. So the value differs by install path, and nothing about the failure looks like a permissions problem: the tool works, it just shows a roster nobody can identify. A tool that has to match physical work, scanned papers or handwritten submissions, to students is unusable below public.
Note that Deep Linking is not one of these toggles. It is enabled by configuring a placement whose message type is LtiDeepLinkingRequest, so a key with every service scope enabled and no deep linking placement still gives the instructor nowhere to select content.
How to build an LTI 1.3 Advantage tool that works with Canvas
The OIDC-based launch flow has five distinct steps, and each one has a failure mode worth knowing before you write a line of code.
Minimal pseudocode: validating the id_token
# Step 1: Receive id_token POST at redirect URI
id_token = request.POST["id_token"]
state = request.POST["state"]
# Step 2: Verify state matches stored value (CSRF protection)
assert session_store.get("lti_state") == state
# Step 3: Fetch the platform's JWKS and validate the JWT.
# The issuer is a property of the platform instance, not of Canvas as a product:
# Instructure-hosted Canvas issues https://canvas.instructure.com, while
# self-hosted and some regional deployments issue something else. Validate
# against the issuer you recorded when the tool was registered, never a constant.
platform = registrations.lookup(iss=unverified_iss(id_token), client_id=CLIENT_ID)
jwks = fetch_json(platform.jwks_url)
claims = jwt.decode(
id_token,
jwks,
algorithms=["RS256"],
audience=CLIENT_ID,
issuer=platform.iss
)
# Step 4: Validate deployment_id
assert claims["https://purl.imsglobal.org/spec/lti/claim/deployment_id"] \
in REGISTERED_DEPLOYMENT_IDS
Storing the issuer, client ID, and JWKS URL per registration is also what lets one deployment of the tool serve several Canvas instances, and what keeps a tool working when an institution moves from a hosted Canvas to a self-hosted one.
Deep Linking response (Content-Item)
When Canvas launches your tool in Deep Linking mode, the message_type claim is LtiDeepLinkingRequest. Your tool presents a content picker, and when the instructor selects content, you POST a signed JWT back to the deep_link_return_url from the claims.
# Minimal Deep Linking response JWT payload.
# "aud" is the platform's issuer, taken from the launch you are answering,
# not a hardcoded Instructure hostname.
{
"iss": CLIENT_ID,
"aud": platform.iss,
"iat": now(),
"exp": now() + 600,
"nonce": generate_nonce(),
"https://purl.imsglobal.org/spec/lti-dl/claim/data": dl_data,
"https://purl.imsglobal.org/spec/lti-dl/claim/content_items": [
{
"type": "ltiResourceLink",
"title": "Assignment Title",
"url": "https://tool.example.com/launch",
"lineItem": {
"scoreMaximum": 100,
"label": "Assignment Title",
"resourceId": "assignment_001"
}
}
]
}
Including a lineItem in the Deep Linking response tells Canvas to create a gradebook column automatically, with no separate AGS LineItem creation call needed. Give it a resourceId you control, as above, because that is the handle you will use later to find the column Canvas built rather than creating a second one. A content item that opens the tool outside the Canvas frame can also say so here, either with the spec's window: { "targetName": "_blank" } or Canvas's windowTarget shortcut, which is worth doing for anything richer than a static page.
Key developer constraint: Canvas requires that your OIDC initiation handler issues a browser redirect. Any attempt to complete the OIDC handshake server-to-server will fail because Canvas validates the user's active browser session as part of the flow. This is the most common cause of "silent" launch failures where the tool receives no error but the launch never completes.
Pro Tip: Safari and some privacy-focused browsers block third-party cookies, so every cookie your tool sets inside the Canvas iframe is dropped: the state cookie, and any session cookie the launch mints. There are two ways out and they are not exclusive. Implement the Platform Storage postMessage API, lti.put_data before the OIDC redirect and lti.get_data after the id_token POST, so state never rides on a cookie. Or take the launch out of the frame, with windowTarget on the content item and a new-window display type on the placement, so the tool answers on a top-level window where its cookies are first party. Tools that hold a real session, rather than rendering one page per launch, usually need the second one regardless of what state uses.
The community reference implementation at doldsimo/lti-1.3-canvas-lms provides working launch handling and JWT validation code you can adapt as a starting point.
| Endpoint | Your responsibility | Canvas's role |
|---|---|---|
oidc_initiation_url | Accept login params, generate state/nonce, redirect to Canvas auth | Sends login hint, client_id, lti_message_hint |
| OIDC auth endpoint | Construct auth request URL | Validates request, returns id_token via POST |
| Redirect URI | Validate state, verify JWT, render content | POSTs id_token and state |
| JWKS URL | Serve your public keys (for signing DL responses) | Fetches to verify tool-signed JWTs |
| AGS / NRPS endpoints | Call Canvas service URLs from JWT claims | Enforces scope, returns data or accepts scores |
How grade return (AGS) and other LTI Advantage services map to Canvas
AGS is what makes an LTI tool a graded activity rather than just an embedded page. When you enable AGS on the Developer Key and include a lineItem in your Deep Linking response, Canvas creates a gradebook column. Your tool then POSTs scores to that column using the AGS Score API.
Service overview
| Service | What it does in Canvas | Required scope |
|---|---|---|
| AGS LineItems | Creates and updates gradebook columns | https://purl.imsglobal.org/spec/lti-ags/scope/lineitem |
| AGS LineItems (read-only) | Finds the column Canvas already created for a resource link | https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly |
| AGS Scores | Submits student scores to a column | https://purl.imsglobal.org/spec/lti-ags/scope/score |
| AGS Results | Reads existing scores (read-only) | https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly |
| NRPS | Retrieves course roster with roles | https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly |
| Deep Linking | Lets instructor select content, creates line items | No scope. Enabled by a placement with message_type: LtiDeepLinkingRequest |
| PNS | Receives async notifications (roster changes, etc.) | https://purl.imsglobal.org/spec/lti/scope/noticehandlers, plus a registered handler URL |
The lineitem.readonly row is the one that gets dropped from key configurations, because a tool that creates its own columns looks like it has no reason to read them. It has: finding the column Canvas already built is a read, and it is the difference between grades landing in the gradebook and grades landing in a column nobody can see. See the note below.
Posting a score via AGS
The lineItem URL comes from the https://purl.imsglobal.org/spec/lti-ags/claim/endpoint claim in the launch JWT. Use it directly, do not construct it manually.
POST {lineItem_url}/scores
Authorization: Bearer {access_token}
Content-Type: application/vnd.ims.lis.v1.score+json
{
"userId": "student_canvas_id",
"scoreGiven": 87,
"scoreMaximum": 100,
"comment": "Step 3 correct; step 4 missing unit conversion.",
"activityProgress": "Completed",
"gradingProgress": "FullyGraded",
"timestamp": "2026-03-15T14:30:00Z"
}
The access_token comes from Canvas's OAuth 2.0 client credentials endpoint using your client_id and private key. Canvas processes score submissions asynchronously: a 200 OK response confirms receipt, but the score may take a few seconds to appear in the gradebook. Send scoreMaximum explicitly on every score. It saves a round trip discovering it from the line item, and it means a score can be posted with only the score scope in hand.
Practical note on Deep Linking and AGS together: When an instructor selects content via Deep Linking and your response includes a
lineItemobject, Canvas creates the gradebook column at that moment. Subsequent AGS score POSTs to that line item's URL will populate the column without any additional setup. This is the cleanest path to grade-return: one Deep Linking response handles both content placement and gradebook column creation.
Resolve the line item, do not assume you created it. There is a second route into the same integration, and it is the one instructors take more often: they create an assignment in Canvas, set the submission type to External Tool, and point it at your URL. Canvas then makes the line item itself and wires it to the gradebook column, before your tool has seen a single launch. A tool that responds by creating its own line item gets a
201, posts scores that are accepted with a200, and none of it ever appears in the gradebook, because the column the assignment is backed by is the other one. Before creating, ask the line items endpoint for the ones on this resource link, and for a deep-linked assignment that has not been launched yet, ask by theresourceIdyou stamped on the content item. Treat an unfiltered answer as no answer:resource_idis an optional filter, and a platform that ignores it hands back every column in the course, so picking the first result posts one assignment's grades into a neighbouring assignment's column.
Pro Tip: Do not trim the scope list a tool asks for as a compliance gesture. Canvas grants scopes to the access token as a set, and a token request for a scope the key does not carry fails whole, with a 400 that names nothing, so removing lineitem.readonly from a tool that needs it to find its column produces a total AGS outage that reads like a signing problem. Ask the vendor which services the integration genuinely uses, then enable that list exactly. Scope review belongs at procurement, not at the toggle.
NRPS is useful when your tool needs to pre-populate a roster before students launch. Call the context_memberships_url from the NRPS claim with a Bearer token scoped to contextmembership.readonly. Canvas returns a paginated list of members with their roles, Instructor, Learner, TeachingAssistant, mapped to the LTI role vocabulary. Follow the next link rather than requesting an unbounded page, and give the walk a page ceiling and a deadline: a partial roster is worse than a slow one, because a member missing from page four looks exactly like a student who dropped the course.
What NRPS returns about each member is governed by the Developer Key's privacy level, not by the NRPS scope. With the scope enabled and the privacy level below public, every call succeeds and every member comes back without a name or an email.
Testing checklist and common launch/grade errors
Pre-launch checklist
- Developer Key is set to On in Canvas Admin > Developer Keys.
- Tool is added as an external app at account or course level using the correct Client ID.
- JWKS endpoint returns a valid JSON key set and is publicly reachable (test with
curl). - Redirect URI in the Developer Key exactly matches the URI the tool sends in its OIDC auth request, character for character, including protocol, port, and trailing slashes.
- Every scope on the tool's documented list is enabled on the Developer Key, with none left off as a precaution.
- Privacy level set on the Developer Key, and a test launch confirms the name and email you expect actually arrive.
- Both content routes exercised: an assignment created through Deep Linking, and an assignment created in Canvas with submission type External Tool pointed at the same tool. Confirm a score reaches the gradebook in each, and that neither leaves a second, empty column behind.
- Test launch performed in Canvas beta or test environment before production.
Common errors and fixes
| Error | Likely cause | Fix |
|---|---|---|
redirect_uri_mismatch | URI in Developer Key differs from tool's OIDC request | Compare both values character by character, including port; query parameters are not part of the comparison |
invalid_id_token / signature failure | JWKS URL unreachable or wrong key | Confirm JWKS URL is public; check key ID (kid) matches |
Missing deployment_id claim | Tool not added as external app after key creation | Add tool via Settings > Apps > By Client ID |
| Launch fails in Safari only | Third-party cookie blocked | Use Platform Storage lti.put_data / lti.get_data, or launch the tool top level so its cookies are first party |
| Token request or AGS call returns 400 / 401 with no detail | Key's granted scopes are not the set the tool requests | Compare the key's toggles against the tool's documented scope list and enable the whole set, including any read-only ones |
| Score accepted but never appears | Score went to a second line item, not the column the assignment is backed by | Look up the existing line item by resource link or resourceId before creating one; delete the orphan column |
| Score POST returns 422 | Either the student is not in the context, or the Canvas assignment is unpublished | Read the response body, the status alone cannot separate them; publish the assignment and retry |
| Score POST returns 404 on a URL that used to work | Gradebook column was deleted or the assignment was rebuilt in Canvas | Discard the stored line item URL and re-resolve or recreate the column |
| Score POST returns 409 | Timestamp is not newer than the score Canvas already holds | Stamp scores at a resolution finer than your retry loop, and delay retries |
| Roster and launches carry IDs but no names | Developer Key privacy level below public | Set the privacy level on the key, then re-launch; the scopes are not the problem |
| Scope change appears not to take effect | Canvas caches developer key configuration | Re-save the key through the UI or API rather than editing underlying data, then re-install the app |
| Deep Linking response rejected | JWT signed with wrong key or expired | Verify exp claim; confirm tool signs with its own private key |
Pro Tip: When diagnosing launch failures, open your browser's network tab and capture the full OIDC redirect chain. The id_token POST to your redirect URI is visible there, and you can decode the JWT payload at jwt.io to inspect every claim before your server-side validation runs. This saves significant debugging time compared to reading server logs alone.
- For role propagation issues, launch as a student in a test enrollment and inspect the
https://purl.imsglobal.org/spec/lti/claim/rolesclaim. Canvas maps its internal roles to LTI role URIs:http://purl.imsglobal.org/vocab/lis/v2/membership#Learnerfor students,#Instructorfor teachers. - For grade-sync failures, confirm the
lineItemURL in your score POST matches the one returned in the launch JWT, not a manually constructed URL. - For NRPS failures, verify the
context_memberships_urlclaim is present in the launch JWT (it only appears when NRPS is enabled on the Developer Key).
Security and student-data privacy considerations for Canvas LTI integrations
Security in an LTI integration is not optional configuration. It is the foundation that makes the trust model work. Every endpoint in the flow must use HTTPS. Any tool that accepts an id_token over plain HTTP is vulnerable to token interception, and Canvas will not initiate launches to non-HTTPS endpoints in production.
Minimum security checklist:
- All tool endpoints (OIDC initiation, redirect URI, JWKS, AGS/NRPS handlers) served over HTTPS with a valid certificate.
- JWKS hosted at a stable, publicly reachable URL; implement key rotation on a defined schedule (annually at minimum) and support multiple active keys during the rotation window so in-flight launches do not fail.
- Client secrets and private keys stored in environment variables or a secrets manager, never in source code or version control.
- Least-privilege services: agree with the vendor on which Advantage services the integration genuinely uses, then enable that scope list in full. Withholding part of a service's scope set does not narrow the tool's access, it disables the service and produces an error that names nothing.
nonceandstateparameters validated on every launch to prevent replay attacks.expandiatclaims validated; reject tokens older than a few minutes.
Privacy and FERPA guidance:
FERPA applies to any tool that receives student educational records, which includes names, user IDs, enrollment data, and grades. Before enabling a tool that receives NRPS roster data or AGS scores, administrators should confirm that a data processing agreement (DPA) is in place with the vendor. The DPA should specify data retention limits, access controls, and breach notification procedures. Our K–12 student data privacy compliance guide covers the clauses these agreements need in more depth.
Minimize PII in custom LTI claims. Canvas allows administrators to add custom parameters to a Developer Key, but each custom parameter that includes student data expands the tool's data footprint. If the tool only needs a pseudonymous identifier to correlate launches with scores, use Canvas's $Canvas.user.id substitution variable rather than passing name or email.
The privacy level is the real control here, and it is a decision about the tool rather than a default to leave alone. A tool that only needs to correlate launches with scores can run at name_only or lower, and should. A tool whose job requires a human to identify whose work is on the page, anything grading scanned or handwritten submissions, cannot function below public, and setting it there is a deliberate, documented disclosure covered by the DPA rather than an oversight. Decide which of the two you have before the key is created, because discovering it later means a re-install and a roster that has to be rebuilt.
Pro Tip: Grant AGS only when grade sync is a core requirement of the tool's function. A tool that embeds reference content does not need AGS. A tool that embeds a graded quiz does. Reviewing service assignments annually, especially after vendor updates, is a practical way to keep the integration's data permissions aligned with its actual use.
Practical examples: config URL flow and an Assignify grading tool walkthrough
What a provider's configuration URL/JSON contains
When a tool provider gives you a registration URL or JSON file, it typically encodes all the fields Canvas needs for the Developer Key in a single payload:
oidc_initiation_urltarget_link_uri(default launch URL)public_jwk_url(or a staticpublic_jwk)scopes(requested Advantage services, as an array)extensionscarrying the Canvas-specific block:domain,tool_id,privacy_level, andsettings.placements(course navigation, assignment selection, link selection, and so on)
Be aware that there are two different documents in circulation and Canvas reads only one of them. The 1EdTech registration document, which a dynamic registration exchange uses, names these fields initiate_login_uri, jwks_uri, a space-delimited scope string, and nests everything else under https://purl.imsglobal.org/spec/lti-tool-configuration. Canvas's own Developer Key JSON uses the names in the list above. Neither is a superset of the other, so a provider serving one has translated it for the other, and pasting the wrong one into the Developer Key screen fails in ways that look like a malformed file. If the provider offers a configuration URL, prefer it over pasting, since it is the copy they keep current.
Pasting this JSON into the Developer Key creation screen or entering the URL under "Enter URL" auto-populates every field, including the placements and the privacy level that manual entry leaves at a default. The admin still reviews and enables the key, auto-import does not bypass the approval step, but it eliminates the transcription errors that plague manual entry.
Assignify in a Canvas LTI workflow
Assignify uses Deep Linking and AGS together to create a grading workflow that lives entirely inside Canvas. When an instructor adds an Assignify assignment to a Canvas course, the Deep Linking launch presents the assignment configuration interface. The instructor selects the assignment and grading rubric; Assignify's response JWT includes a lineItem that Canvas uses to create the gradebook column automatically.
When a student submits handwritten work, Assignify grades it against the rubric, checks the result before presenting it, and produces step-by-step feedback for the instructor to review. Grades reach the gradebook when the instructor releases them, through the AGS Score API, so nothing unreviewed is ever pushed to Canvas and there is no export step once it is. Students launch straight from Canvas without creating a second login. For institutions managing large STEM cohorts, this means automated handwritten grading that preserves full instructor visibility in Canvas.
Staging vs. production redirect changes: When you move Assignify (or any tool) from a sandbox to production, update the Developer Key's redirect URIs before the first production launch. The safest approach is to add the production URI to the existing key while keeping the staging URI active, test the production launch, then remove the staging URI. Removing the staging URI first will break any active sandbox testing.
Pro Tip: A minimal smoke-test workflow for any LTI grading tool: (1) instructor launches Deep Linking, selects content, confirms exactly one gradebook column appears; (2) student launches the assignment, completes it; (3) grade it and check the Canvas gradebook, polling rather than trusting a fixed wait. Then run the whole thing again against an assignment created in Canvas with submission type External Tool, which is a different code path on the tool's side and the one that produces duplicate columns. If a score does not appear, count the line items on the assignment before touching Canvas configuration: an accepted score with an empty gradebook is almost always the second column, not a permissions problem.
One-page quick checklist for admins and developers
Administrator checklist
- Developer Key created and set to On in Admin > Developer Keys.
- The tool's full documented scope list enabled, including read-only AGS scopes, with nothing withheld.
- Privacy level set deliberately, and a real launch confirms the identity data the tool needs actually arrives.
- Tool added as external app at account or course level via Client ID.
- Placements configured. Course navigation for a course-level entry point, assignment selection and link selection for Deep Linking. Link selection is what backs an External Tool item added to a module.
- Test course created with at least one instructor and one student enrollment.
- Launch tested in Canvas beta/test environment; redirect URI confirmed exact match.
- Data processing agreement reviewed with vendor before enabling NRPS or AGS.
Remediation hints: If the tool does not appear in course navigation, check that the course navigation placement is enabled on the Developer Key. If the launch fails immediately, the most likely cause is a redirect URI mismatch. Compare the Developer Key value with what the tool sends in its OIDC request. If launches work but the tool shows nobody's name, the privacy level is the setting to change, not the scopes.
Developer checklist
- OIDC initiation endpoint returns a browser redirect (HTTP 302) to Canvas's auth endpoint, confirmed with a real browser request, not a server-side HTTP client.
- JWT validation passes: signature verified against the platform's JWKS,
issmatches the issuer recorded for that registration rather than a hardcoded constant,audmatches yourclient_id,expis in the future. - JWKS endpoint publicly reachable and returns a valid JSON key set, confirmed with
curlfrom an external network. Confirm the platform can reach it too: a launch only needs the browser, but every AGS and NRPS call has the platform fetch your keys server side, which is why a tool can launch perfectly and then fail at the first service call. stateandnonceparameters generated, stored, and validated on every launch.deployment_idclaim validated against registered values.- AGS score POST returns
200 OKin test environment, and the score is visible in the Canvas gradebook, not merely accepted. - Deep Linking response JWT signed correctly; Canvas creates the gradebook column on instructor content selection, and the tool adopts that column rather than creating its own.
- Service claims persisted from the launch. AGS and NRPS calls happen days after the launch that carried their URLs, so anything relying on a live session or a short-lived cached context passes in testing and fails in week three.
- Third-party cookie handling verified in Safari, either through Platform Storage or by launching top level.
Remediation hints: A 400 or 401 on AGS calls almost always means the granted scope set does not match the requested one, and the message will not say which scope is missing, so compare the lists rather than reading the error. A Deep Linking response that Canvas rejects is usually a JWT signing error. Verify you are signing with your tool's private key, not Canvas's.
Key Takeaways
A working Canvas LTI integration requires LTI 1.3 / Advantage, a correctly configured Developer Key with exact redirect URI matching, a deliberate privacy level, verified OIDC/JWT flows, and the tool's full scope list rather than a trimmed one.
| Point | Details |
|---|---|
| Use LTI 1.3 / Advantage | LTI 1.3 with OIDC/JWT is the required standard for new Canvas integrations; LTI 1.1 lacks AGS and NRPS. |
| Exact redirect URI matching | Canvas matches the redirect URI up to the query string exactly; a single trailing slash or a missing port breaks every launch. |
| Choose services, then grant them fully | Decide which Advantage services the tool needs, then enable that scope list in full. A partial set does not restrict the tool, it disables the service with an error that names nothing. |
| Set the privacy level | Below public, launches and rosters arrive without names or emails, and nothing about the failure looks like a configuration problem. |
| Resolve line items, do not duplicate them | When Canvas already created the column, a tool that creates its own posts scores into a column the gradebook never shows. |
| Test in beta/test first | Always validate Developer Key config, JWKS, and AGS calls in Canvas beta before touching production. |
| Assignify for STEM grading | Assignify uses Deep Linking and AGS to return step-by-step feedback and released scores directly into the Canvas gradebook. |
Why the LTI rollout conversation is missing a key step
Most LTI deployment guides stop at "get the launch working," and that is exactly where the real operational risk begins. A launch that works in a demo environment and a launch that works reliably across 500 student submissions in week three of a semester are different problems.
The piece that gets skipped most often is the grade-sync monitoring window. AGS score POSTs are asynchronous, and Canvas processes them in a queue. Under normal load, scores appear quickly. Under peak load, end-of-term submission rushes for example, that queue can back up, and a tool pushing a whole cohort is throttling itself against the platform's rate limits at the same time. Instructors who check grades immediately after a submission window closes may see incomplete data and assume the integration is broken. Part of the fix is communication: set expectations with faculty before the first graded activity goes live. The other part is insisting the tool reports its own passback outcomes. A tool that pushes grades silently and shows no per-student failures leaves the instructor to discover an empty gradebook weeks later, and by then nobody can tell a queue delay from a student the platform never recognized.
The second underestimated issue is the Safari / third-party cookie problem. Most institutions test integrations in Chrome on a managed device. Safari is common among students on personal MacBooks and iPads. A tool that neither implements Platform Storage nor takes its launch out of the Canvas frame will silently fail for a meaningful portion of your student population, and those students will report it as a Canvas bug, not a tool bug. Ask the vendor which of the two approaches they use, and verify it in Safari before any student-facing rollout, not after the first support ticket.
On the policy side: data processing agreements are not a legal formality. They define what happens to student grade data if the vendor is acquired, changes their data retention policy, or experiences a breach. Reviewing DPA terms before enabling AGS is the kind of step that takes 30 minutes and can prevent a FERPA incident that takes months to resolve.
Stage your rollouts by department. A single pilot course with a willing faculty member reveals configuration issues, role-propagation edge cases, and grade-visibility gaps before they affect an entire institution. The first grading cycle is your real integration test.
Assignify brings LTI-ready AI grading directly into your Canvas gradebook
STEM educators managing large cohorts know the specific weight of a grading backlog: handwritten problem sets, multi-step proofs, lab reports that require line-by-line review. Assignify was built for exactly that workload. It connects to Canvas via LTI Advantage, uses Deep Linking so instructors can assign work directly from the Canvas course, and returns step-by-step AI-generated feedback and scores through AGS, so grades reach the Canvas gradebook the moment the instructor releases them, with no export step and no second column to reconcile.
What Assignify automates goes beyond a single score: every grade is checked against the rubric and revised before an instructor ever sees it, analytics dashboards surface class-wide performance patterns, and the feedback annotations give students the kind of detailed, step-wise guidance that would take an instructor hours to write by hand. For institutions piloting the tool, testing in a sandbox course first lets your team validate the Deep Linking flow, confirm AGS score delivery, and review the feedback quality before any student-facing rollout.
Apply for beta access to set up a test course and run the full LTI Advantage workflow, launch, grade return, and analytics, before committing to institution-wide deployment.
Authoritative sources for Canvas and LTI developer documentation
The sources below cover the full implementation path, from initial concept to production debugging.
- Canvas external tools and LTI introduction
- LTI Developer Key configuration
- LTI launch overview
- What are External Apps (LTI Tools)?
- Canvas LMS: Learning Tools Interoperability (LTI)
- doldsimo/lti-1.3-canvas-lms reference implementation
- The Ultimate Guide to AI for Educators: Automating Handwritten Grading in 2026
- The Best LMS Grading Tools for Handwritten STEM Assignments in 2026
- LTI Integration in Canvas, Fox Online & Digital Learning
Statistic callout: 1EdTech's LTI Advantage specification defines three mandatory extension services, AGS, NRPS, and Deep Linking, as the baseline for any tool claiming full Advantage compliance. Implementing all three, rather than just the launch flow, is what separates a grade-aware integration from a simple embedded link.
Want to see Assignify in action?
Evaluate how specialized visual intelligence integrates with your current curriculum. Request a technical workflow briefing with our system architecture team.
Frequently Asked Questions
Common questions about grading with AI and handling handwritten student submissions.
LTI 1.1 uses OAuth 1.0 signatures and XML messages with a shared secret, and it has no standardized grade-return API or roster service. LTI 1.3 uses an OIDC-based launch flow with JWT payloads signed by Canvas's private key and verified against Canvas's published JWKS, and it adds LTI Advantage services: Assignment and Grade Services (AGS), Names and Roles Provisioning Service (NRPS), and Deep Linking. Migrating a tool from 1.1 to 1.3 requires re-registering it under a Developer Key and updating existing course-level placements.
Canvas compares the redirect URI registered on the Developer Key against the URI the tool sends in its OIDC auth request, matching everything up to the query string exactly. A trailing slash, an http versus https difference, a missing port, or a different subdomain will break every launch, while query parameters on the launch URL are ignored by the comparison. Compare both values character by character and update the Developer Key.
Through AGS. Enable the AGS scopes on the Developer Key, then include a lineItem object in your Deep Linking response so Canvas creates the gradebook column at content selection time. The tool POSTs scores to the line item URL taken from the lti-ags/claim/endpoint claim in the launch JWT, authenticated with a Bearer token from Canvas's OAuth 2.0 client credentials endpoint. If the instructor built the assignment in Canvas and pointed it at the tool, Canvas has already created the column, so the tool must look the existing line item up rather than create a second one. Canvas processes scores asynchronously, so a 200 OK confirms receipt but the grade can take a few seconds to appear.
Safari and other privacy-focused browsers block third-party cookies, so any cookie the tool sets inside the Canvas iframe is dropped, which breaks cookie-backed state and any session the tool mints during the launch. There are two accepted fixes. Use the LTI Platform Storage postMessage API, lti.put_data before the OIDC redirect and lti.get_data after the id_token POST, so state never depends on a cookie. Or launch the tool top level rather than framed, using windowTarget _blank on the content item or a new-window display type on the placement, so its cookies are first party. Institutions that test only in Chrome on managed devices usually discover this after the first student support ticket.
Confirm a data processing agreement is in place with the vendor covering data retention limits, access controls, and breach notification, since FERPA applies to any tool receiving names, user IDs, enrollment data, or grades. Then enable exactly the scopes the tool's documentation lists, no more and no fewer. Trimming a scope the tool actually requests does not degrade it gracefully: Canvas refuses the whole access token request, and the tool sees an opaque 400 that names no scope, so the failure gets misdiagnosed as a broken key.
The Developer Key's privacy level controls it. At anything below Public, Canvas strips name and email from both the launch JWT and the NRPS roster, so the tool receives opaque user IDs and cannot match students to their work. Manual entry defaults to the most restrictive level, and dynamic registration infers one from the claims the tool asks for, so set the privacy level explicitly on the key and re-launch to confirm names arrive.