Skip navigation
All Places > Canvas Developers > Blog > 2017 > June



As Instructure's Partner Integrations Specialist, I’m often asked how an LTI tool can return a grade to Canvas for an assessment that’s completed on the tool provider side. The answer varies based on a set of requirements the tool provider has. However, I’ve outlined the most common workflows I encounter to hopefully help others find the right place to start—keep in mind that a combination of these workflows can also be utilized, you don’t have to choose just one.


LTI-only Workflow



This approach requires an LTI integration to be configured with an assignment_selection placement and a content item selection request. It should also be capable of executing the LTI Outcomes service.


  • API access not required
  • Interoperable
  • Can return LTI Launch URL as submission, allowing the tool to iframe a submission on the submission details page and within Speedgrader without the user having to leave Canvas
  • Grades applied using the LTI Outcomes service will also mark the student as having completed the assignment; this isn’t the case when a grade is returned via Submissions API



  • Teacher must manually configure each external tool assignment using content item selection
  • Student must launch each assignment so the tool can collect required information
    • As a result, a tool can’t have a series of assessments where grades are returned to separate assignments unless they provide the content as a common cartridge


Instructor Workflow

  1. Create an assignment in Canvas
  2. Choose “External Tool” as the Submission Type
  3. Choose the tool from the list that appears in the modal
  4. A properly configured tool allows the instructor to then choose a resource
  5. Tool returns a content item message to Canvas with the Launch URL to that resource
  6. Teacher saves and publishes the assignment

Student Workflow

  1. Launch the assignment triggering a launch to the launch URL provided by the tool when the teacher configured the tool
  2. Tool consumes the lis_outcome_service_url and lis_result_sourcedid for the student X assignment combination and stores it
  3. Student completes the assessment
  4. Tool either automatically grades the student, or waits for teacher to grade student
  5. Grade is returned to Canvas via LTI Outcomes service
  6. (optional) Using the LTI Outcomes service, the tool can also return a piece of plain text, a basic URL, or even an LTI Launch URL. This will be attached to a student submission object in Canvas, and it’ll be visible on the student submission page and in Speedgrader


LTI + API Approach

There are two flavors of this workflow. One uses the LTI Outcomes service for grading, while the other uses the Canvas submissions API.


This approach requires an LTI integration to be configured with at least a course_navigation placement. It also must use OAuth2 to obtain teacher and student tokens.



  • Allows a single launch point from Canvas (usually from course navigation) where a student can complete many assessments and have the tool provision assignments within Canvas on the fly and grade them as needed
  • Less manual work for the teacher
  • Organization of assessments can be offered either on the tool side, or by leveraging the Modules page in Canvas
  • Content-Item not required
  • Tools can create external tool assignments and use the API to simulate a student launching the tool in order to obtain data required for the LTI Outcomes service



  • Requires OAuth2 to obtain either a Canvas API token for teachers (to grade) and students (to optionally submit)
  • Requires heavy usage of Canvas specific API’s, so is not interoperable
  • If grading is achieved via submission API, submissions are not marked complete after a grade is applied. The tool must also submit to the assignment using the submission API to mark complete. Furthermore, only plain text or URL’s can be provided


Instructor Workflow

  1. Instructor launches the tool from course navigation or elsewhere and assigns the available work to students, or the tool can do this automatically
  2. Tool kicks off OAuth2 if it needs to obtain or refresh a teacher token
  3. Tool creates external tool assignments using the Assignments API. These assignments should have an LTI Launch URL that has query parameters for the assignment, or the path that launches directly to the assignment, e.g.<tools_assignment_id> or<tools_assignment_id>


Student Workflow

  1. Student launches the tool from the course navigation button
  2. Tool kicks off OAuth2 if it needs to obtain or refresh a student token
    1. Student picks the assignment within the tool and completes it
    2. Tool grades and submits the assessment to Canvas
      • Option 1: LTI for grading
        • Tool uses the sessionless launch API as the student to launch the tool
        • Tool consumes the lis_outcome_service_url and lis_result_sourcedid for the student X assignment combination and stores it
        • Tool uses the IMS Outcomes service to return a grade and an LTI Launch URL to their submission
      • Option 2: API for grading
        • Tool uses the Submissions API as the teacher to grade the student
        • (optional) Tool uses the Submissions API as the student to submit so the teacher can verify students completed the assessment


    NOTE: The process can be further automated/simplified by obtaining an admin token with the “become other users” permission. This token can then be used to make API requests on behalf of teachers and students by appending the as_user_id=<student/teacher user id>. However, this can be problematic for high numbers of assignments or students as you’ll likely experience throttling issues.

    I always like to hear about creative ways for integrations to grade and submit assignments. How has your integration combined API and LTI to grade and submit to Canvas?

    The Challenge

    Some of the students at our institution completely overlook the View Feedback link that appears when they are viewing a previously submitted assignment upload. Many simply click on the download link for the file, and are somewhat surprised when they don't see any comments/markup on the document. The issue stems partially from the inline placement of the two elements, as shown in the image below.


    Many students think that the View Feedback link is an extension of or an additional comment to the Document Download link that precedes it.


    Current Submission Page


    Additionally, should the student actually click on the View Feedback link, he or she then renders the document in question, with its markup in a container that is a fraction of the browser window size. The student does have the option to close the course section menu to provide additional real estate, and the document view can be resized, but its a lot of additional steps that the student has to go through to get the document rendered in an orientation that is more suitable to him or her.



    I wanted to provide the student with a cleaner User Interface and User Experience. As such, I wrote custom javascript and css, and developed a couple of icons to achieve the following:

    1. Differentiate between the Document Download button and the View Feedback button.
    2. Once the student has clicked the View Feedback button, there is a button in the window that appears that allows the student to take the document view container to fullscreen (minus the global nav bar) and back.


    New Button Styles for download and feedback


    This solution utilizes custom CSS and javascript/jQuery. It is also worth mentioning that I have focused solely on assignment submissions, and have not gotten the opportunity to test this solution across the entire LMS. Additionally, I did not have the opportunity to test this solution with TurnItIn, which generates a link between the document download button and the view feedback button. In the future, I will issue an update if I am able to see what kind of content is generated when the student turns an assignment in to TurnItIn.


    Doc View Full Screen Mode





    If you are interested in applying this to your environment, please feel free to fork or download from my Bitbucket repository, which can be found here.

    Update 2018-07-25

    I know this is late, but I was unable to edit the post to make this update.


    On 2018-06-23 this script became OBSOLETE due to Instructure having made their own version a non-optional component of the official UI.


    I have recieved a few messages inquiring about using the script for self-hosted instances of Canvas. The script was never tested with self-hosted Canvas, but should still work so long as the new UI is disabled. However, self-hosted Canvas was never supported, thus the status of OBSOLETE will be maintained.


    For more information regarding the new UI enforcement, please check out the release notes:


    Update 2018-03-05

    This script is now DEPRECATED due to Instructure having released their own version as part of the official UI. The script will still work so long as the new UI is disabled.


    For more information regarding the new UI, please check out the release notes:


    Original Post 2017-07-09


    This is my first blog post to the Canvas Community, and my first anywhere in nearly 15 years, so my apologies if it is a little off track with what everyone else expects to see. I looked through several frequent posters whose blogs I have used to try and get a feel for what may be considered acceptable formatting. Basically, I concluded that explaining the overall situation, reason for acting, and finally the "solution" developed would be the safest path to take. So, if you feel you have a sufficient understanding on what this post is about from the title (which I have no idea what it will be at the time of my writing this), then feel free to skip to the bottom.


    The Situation

    As everyone knows, the Courses list at the account-level is lacking in one major area: it only lists 100 courses? Yeah, that is right, it is limited to displaying only 100 courses, the selection of which I have not actually figured out. At first, I thought it was a list of the published courses, alphabetically by name, then unpublished courses until 100 were listed, but I later found that the published status means nothing and the alphabetical sorting is inconsistent (i.e., I get "WORKING ENG120" before "WORKING CJ125").


    Furthermore, there is not even an option to view more courses! No pagination, no Show More link, just nothing! Well, that just will not do, and, as it turns out, Instructure agrees! Community Team submitted the idea back in April 2015 to have an Advanced Admin Search & Sort developed, and it was announced in November of that year that the idea was in development. Of course, this idea is for Courses AND Users, but that still includes Courses.


    Well, here we are 20 months later with no idea when this might be found (I understand it is a major undertaking, I am not complaining).


    Reason for Creating

    So, like many others, our development team wanted a more robust Courses list. To be specific, we wanted to be able to see a full list of courses being developed. Of course, we track that outside of Canvas, but it often has its benefits to see what has made it into Canvas at a glance. We also wanted to be able to search using "match all terms", not "match exact string".


    My first choice was to see if another developer had already created such a script. Well, in the idea posted by Community Team, Dave Schober posted about the search system developed by PennState, which looks like a great system, but as Andy Fisher mentioned, their setup relies on internal systems, making it difficult to share.


    Well, the more I searched around, the less I found (I lost some of what I originally found, yeah, that happened). Finally, I just accepted that if we did not want to wait for Instructure to finish development I would have to write it, myself.


    The Solution

    I call it a solution, but it is really a userscript, and anyone that uses userscripts on a regular basis should be able to say that they are not solutions, just workarounds.


    Q: What does this userscript do?
    A: Simply put, it replaces the Courses list provided by Canvas with a full list of courses that fall under a subaccount with support for pagination (10, 25, 50, 100, and All per page). It still supports filtering by Term, sorting by Name, sorting by Creation Date (via the course_id), and filtering by Name. However, the filtering by Name has been modified to allow for search terms, not a search string; sorting by Course Code has been added, and Hide enrollmentless courses has been removed.


    Q: What do you mean by "filtering by Name has been modified to allow for search terms"?
    A: To explain simply, the Canvas-provided filter for Find a Course searched for a fixed string. This meant that if you wanted to find "201701 Miami Sandbox ENG480", you had to supply a fragment of that title that matched exactly (i.e., "201701 Miami", "Miami Sandbox", "Sandbox ENG480". etc.). This modification allows you to search for terms, not a title. So, if you knew the shell you wanted was "201701" and "ENG480", you can submit "201701 ENG480" or "ENG480 201701" and it will come up with the course.


    The modified Course Name filter searches for all terms supplied, so only courses whose Name contains all the terms will be provided.


    Q: Why is Hide enrollmentless courses removed?
    A: Simply put, we do not have need of it. However, that is really only the reason why it was not pursued. The Canvas API options for determining enrollmentless courses is limited to a GET variable defined when making the API call. Due to this information not being available as part of the returned data, additional logic would be needed to provide all courses while supporting the identification of enrollmentless courses.


    Q: Did you really just make an entire sentence and hyperlink?
    A: Yes. Yes I did.


    Q: Do you think Hide enrollmentless courses will be added to this userscript at some point?
    A: Maybe. It is not like adding the logic for it would be particularly difficult, it just does not serve sufficient purpose to us to be considered worth adding that extra logic.


    Q: Is there anything else that was removed? Is so, why were they removed?
    A: Yes. Yes this is.

    • Student Count: This information is not provided using the /api/v1/accounts/:account_id/courses API call.
    • Enrolled Teachers: This information is not provided using the /api/v1/accounts/:account_id/courses API call.

    Yes, this information could easily be retrieved with additional calls, but that would significantly increase the number of API calls which then slows done the processing time required to generate the course list.


    Q: You mentioned the speed at which the course list is generated would be slowed down by making the additional API calls needed to display more information. What are we talking about? 5-seconds? 10-seconds?
    A: Minutes to hours; entirely dependent upon the connection, number of courses, and how many additional calls are required to pull all the desired information. I, honestly, have not looked into this beyond seeing that it is not provided right away.


    Q: Okay, that makes sense. So how long will this userscript take to run?
    A: That depends on your connection to the server, the load on the server, and how many courses fall under the subaccount. A couple hundred can be retrieved in a matter of seconds, but thousands can take minutes.


    Q: Why is it so slow?
    A: It is slow because the Canvas API forces a pagination limitation of 100 results per call. So, if there are 4783 courses under a subaccount, 48 API calls are needed to retrieve the information for all of them. It may not seem like much, but it adds up, fast.




    How It Works

    1. Load the userscript to your Userscript Manger of choice
    2. Enabled the userscript
    3. Access the "Courses List" of any subaccount in the Canvas LMS
    4. Use the filters to narrow your results
      • Course Name: Accepts multiple terms and compares them against the Course Name of all shells for matches of all terms (case-insensitive) for a complete match. (i.e., If you search for "WORKING 201701", it'll return "201701 Miami Working ENG480" because it contains "WORKING" AND "201701", but it won't return "201701 Miami Sandbox ENG480" because it is missing the "WORKING" term.)
      • Teacher(s): Accepts multiple terms and compares them against all Teacher(s) of each shell (case-insensitive) for a complete match. (i.e., If you search for "Smith Jones", it'll return courses with: "Aaron Smith" and "Jones McMillion"; "James Smithemson" and "Eric Jones"; "Smith Jones"; "Jones Smith")
      • Term Filter: This is literally a clone() of the default Canvas term filter, but it now serves this system (which doesn't have to reload the page when changing filters).
      • Sort By: Mostly a clone of the Canvas sorter, but with the ability to sort by the "Course Code", in addition to the original "Course Name" and "Creation Date" options.
      • Display x Shells: How many courses do you want to see per page? The default is 50, but additional options are 10, 25, 100, and All (All is not recommended for large subaccounts!).






    Related Ideas/Discussions

    Pagination for Courses List (Admin)

    Advanced Admin Search & Sort