If your LTI tool runs activities in Canvas and instructors are manually copying grades from your tool into the Canvas gradebook - you're solving the wrong problem. AGS handles this automatically. Here's how to implement it.
What you need before you start
AGS requires LTI 1.3. If you're still on LTI 1.1, this won't work - the old outcomes service is a different, more limited mechanism. You also need LTI Advantage enabled on your Canvas developer key.
In Canvas, when you create a developer key for your LTI tool, make sure Assignment and Grade Services is checked under LTI Advantage Services. If it isn't enabled, your AGS API calls will return 401 errors regardless of how correctly you've implemented the service.
Step 1 - Capture the AGS endpoint at launch
When Canvas launches your tool via LTI, the id_token contains everything you need for grade passback. In the LTI claims, look for:
https://purl.imsglobal.org/spec/lti-ags/claim/endpoint
This claim contains the AGS endpoint URL and the scopes your tool has been granted. You need two scopes for full grade passback:
-
https://purl.imsglobal.org/spec/lti-ags/scope/lineitem- to create and manage gradebook columns -
https://purl.imsglobal.org/spec/lti-ags/scope/score- to post scores
Store both the endpoint URL and the scopes from this claim. You'll need them for every AGS call. If you don't capture them at launch time, you'll need to re-launch to get them - there's no way to retrieve them after the fact.
Step 2 - Get an access token
AGS calls are authenticated with an OAuth 2.0 access token, not the LTI id_token. You need to request a token from Canvas's token endpoint using your tool's private key.
The token request is a client credentials grant using JWT assertion:
POST https://canvas.instructure.com/login/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion={signed_jwt}
&scope={space_separated_scopes}
The signed JWT must be signed with your tool's private key and contain your client ID as both the issuer and subject, Canvas's token endpoint as the audience, and a short expiry (under 60 seconds).
Canvas returns an access token valid for 1 hour. Cache it and reuse it for multiple AGS calls within that window.
Step 3 - Create a line item (gradebook column)
A line item is a gradebook column in Canvas. You need one line item per gradeable activity.
If your LTI resource link is already associated with a Canvas assignment, Canvas may have created a line item automatically. Check the endpoint URL from the launch - if it contains /line_items/{id}, a line item already exists for this launch. Use it directly.
If you need to create a new line item:
POST {lineitemsEndpoint}
Authorization: Bearer {accessToken}
Content-Type: application/vnd.ims.lis.v2.lineitem+json
{
"scoreMaximum": 100,
"label": "Module 3 Quiz",
"resourceLinkId": "{resourceLinkId from launch}"
}
The resourceLinkId ties the line item to the specific LTI launch context. Include it so Canvas associates the gradebook column with the correct assignment.
Step 4 - Post a score
Once you have a line item URL, posting a score is one API call:
POST {lineItemUrl}/scores
Authorization: Bearer {accessToken}
Content-Type: application/vnd.ims.lis.v1.score+json
{
"userId": "{Canvas user ID from launch}",
"scoreGiven": 85,
"scoreMaximum": 100,
"activityProgress": "Completed",
"gradingProgress": "FullyGraded",
"timestamp": "2026-04-14T10:30:00Z"
}
Canvas updates the gradebook column for that student immediately. The grade appears in the instructor's gradebook without any manual action.
Use the user ID from the LTI launch sub claim - this is the Canvas user identifier that AGS expects. Don't use email addresses or other identifiers.
Common Canvas-specific issues
Scores not appearing in gradebook - check that activityProgress is set to "Completed" and gradingProgress is set to "FullyGraded". Canvas requires both to display the grade. Scores with other progress values may be stored but not shown.
401 errors on AGS calls - your developer key doesn't have AGS scopes enabled, or your access token request is missing the correct scopes. Go back to your Canvas developer key and verify Assignment and Grade Services is enabled.
Line item not found - the line item URL from the launch may have expired or the assignment was deleted in Canvas. Re-launch to get a fresh endpoint URL.
Scores posting for wrong student - you're using the wrong user identifier. Use the sub claim from the LTI id_token, not the custom user data you may have stored in your own system.
FAQ
Can we post grades outside of the LTI launch session?
Yes - that's the main use case. Store the line item URL and access token endpoint from the launch, then post scores whenever the student completes the activity, even hours later. Just request a fresh access token when the old one expires.
Does Canvas automatically create a line item when we create an LTI assignment?
Yes, in most cases. When an instructor adds your LTI tool as an assignment in Canvas, Canvas creates a line item automatically. The launch id_token will contain the existing line item URL in the AGS endpoint claim. Check for it before creating a new one to avoid duplicates.
What's the difference between scoreGiven and scoreMaximum?
scoreGiven is the student's raw score. scoreMaximum is the maximum possible score for the activity. Canvas calculates the percentage and displays it in the gradebook. Always include both - posting scoreGiven without scoreMaximum will cause display issues.
We're posting scores but they appear as zero in Canvas.
Check that scoreGiven is a number, not a string. Also verify that activityProgress and gradingProgress are set correctly. A score of 0 with "FullyGraded" is a valid submission that Canvas will display as 0%.
Top comments (0)