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.

July 26, 202629 min read
AE
By Assignify Editorial Staff
Canvas LTI Integration: A Practical Guide for Admins and Developers

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:

  1. Expose an OIDC initiation endpoint that accepts Canvas's login request and returns a browser redirect (not a server-to-server call).
  2. Host a JWKS endpoint so Canvas can fetch your public keys for JWT verification.
  3. Register one or more redirect URIs that exactly match what you will give the admin for the Developer Key.
  4. 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

  1. Log into Canvas as an account admin and navigate to Admin > [Account Name] > Developer Keys.
  2. Click + Developer Key and select LTI Key from the dropdown.
  3. 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.
  4. 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.
  5. 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)
  6. 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.
  7. 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.

EndpointYour responsibilityCanvas's role
oidc_initiation_urlAccept login params, generate state/nonce, redirect to Canvas authSends login hint, client_id, lti_message_hint
OIDC auth endpointConstruct auth request URLValidates request, returns id_token via POST
Redirect URIValidate state, verify JWT, render contentPOSTs id_token and state
JWKS URLServe your public keys (for signing DL responses)Fetches to verify tool-signed JWTs
AGS / NRPS endpointsCall Canvas service URLs from JWT claimsEnforces 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

ServiceWhat it does in CanvasRequired scope
AGS LineItemsCreates and updates gradebook columnshttps://purl.imsglobal.org/spec/lti-ags/scope/lineitem
AGS LineItems (read-only)Finds the column Canvas already created for a resource linkhttps://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly
AGS ScoresSubmits student scores to a columnhttps://purl.imsglobal.org/spec/lti-ags/scope/score
AGS ResultsReads existing scores (read-only)https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly
NRPSRetrieves course roster with roleshttps://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly
Deep LinkingLets instructor select content, creates line itemsNo scope. Enabled by a placement with message_type: LtiDeepLinkingRequest
PNSReceives 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 lineItem object, 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 a 200, 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 the resourceId you stamped on the content item. Treat an unfiltered answer as no answer: resource_id is 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.

Six-step Assignify Canvas LTI flow: instructor deep-linking, selecting the assignment and rubric, student submission, Assignify auto-grading, the AGS score POST, and the grade appearing in the Canvas gradebook

Testing checklist and common launch/grade errors

Pre-launch checklist

  1. Developer Key is set to On in Canvas Admin > Developer Keys.
  2. Tool is added as an external app at account or course level using the correct Client ID.
  3. JWKS endpoint returns a valid JSON key set and is publicly reachable (test with curl).
  4. 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.
  5. Every scope on the tool's documented list is enabled on the Developer Key, with none left off as a precaution.
  6. Privacy level set on the Developer Key, and a test launch confirms the name and email you expect actually arrive.
  7. 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.
  8. Test launch performed in Canvas beta or test environment before production.

Common errors and fixes

ErrorLikely causeFix
redirect_uri_mismatchURI in Developer Key differs from tool's OIDC requestCompare both values character by character, including port; query parameters are not part of the comparison
invalid_id_token / signature failureJWKS URL unreachable or wrong keyConfirm JWKS URL is public; check key ID (kid) matches
Missing deployment_id claimTool not added as external app after key creationAdd tool via Settings > Apps > By Client ID
Launch fails in Safari onlyThird-party cookie blockedUse 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 detailKey's granted scopes are not the set the tool requestsCompare 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 appearsScore went to a second line item, not the column the assignment is backed byLook up the existing line item by resource link or resourceId before creating one; delete the orphan column
Score POST returns 422Either the student is not in the context, or the Canvas assignment is unpublishedRead the response body, the status alone cannot separate them; publish the assignment and retry
Score POST returns 404 on a URL that used to workGradebook column was deleted or the assignment was rebuilt in CanvasDiscard the stored line item URL and re-resolve or recreate the column
Score POST returns 409Timestamp is not newer than the score Canvas already holdsStamp scores at a resolution finer than your retry loop, and delay retries
Roster and launches carry IDs but no namesDeveloper Key privacy level below publicSet the privacy level on the key, then re-launch; the scopes are not the problem
Scope change appears not to take effectCanvas caches developer key configurationRe-save the key through the UI or API rather than editing underlying data, then re-install the app
Deep Linking response rejectedJWT signed with wrong key or expiredVerify 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/roles claim. Canvas maps its internal roles to LTI role URIs: http://purl.imsglobal.org/vocab/lis/v2/membership#Learner for students, #Instructor for teachers.
  • For grade-sync failures, confirm the lineItem URL in your score POST matches the one returned in the launch JWT, not a manually constructed URL.
  • For NRPS failures, verify the context_memberships_url claim 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.
  • nonce and state parameters validated on every launch to prevent replay attacks.
  • exp and iat claims 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_url
  • target_link_uri (default launch URL)
  • public_jwk_url (or a static public_jwk)
  • scopes (requested Advantage services, as an array)
  • extensions carrying the Canvas-specific block: domain, tool_id, privacy_level, and settings.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

  1. 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.
  2. JWT validation passes: signature verified against the platform's JWKS, iss matches the issuer recorded for that registration rather than a hardcoded constant, aud matches your client_id, exp is in the future.
  3. JWKS endpoint publicly reachable and returns a valid JSON key set, confirmed with curl from 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.
  4. state and nonce parameters generated, stored, and validated on every launch.
  5. deployment_id claim validated against registered values.
  6. AGS score POST returns 200 OK in test environment, and the score is visible in the Canvas gradebook, not merely accepted.
  7. 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.
  8. 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.
  9. 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.

PointDetails
Use LTI 1.3 / AdvantageLTI 1.3 with OIDC/JWT is the required standard for new Canvas integrations; LTI 1.1 lacks AGS and NRPS.
Exact redirect URI matchingCanvas 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 fullyDecide 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 levelBelow 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 themWhen Canvas already created the column, a tool that creates its own posts scores into a column the gradebook never shows.
Test in beta/test firstAlways validate Developer Key config, JWKS, and AGS calls in Canvas beta before touching production.
Assignify for STEM gradingAssignify 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.

Diagram of the operational steps most LTI rollout plans skip, including grade-sync monitoring and Safari cookie fallback

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.

Walk through the Assignify grading workflow yourself, no signup required.

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.

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.

Tags:#Canvas LTI integration#LTI 1.3 Advantage#Canvas Developer Key#AGS grade passback#Deep Linking Canvas#LTI OIDC JWT validation

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.