NAV Navbar
shell
  • Introduction
  • Getting started
  • Authentication
  • LTI
  • Transactions
  • Create a context and sync its roster
  • View a Context's Details
  • Rename a Context ID
  • Archive/Unarchive a Context
  • Delete a Context
  • Summarize Resource Links in Contexts
  • Create a Resource Link and Context
  • View a Resource Link's Details
  • List Resource Links in a Context
  • Get Current Status for Resource Link Work
  • Rename a Resource Link ID
  • Update a Resource Link
  • Delete a Resource Link
  • Transfer a Student Between Contexts
  • View a Student's Answer History
  • Score an Essay
  • Fetch a User
  • Delete a Student
  • Describe a prompt's rubric
  • Managing Credentials and Groups
  • Generating Data
  • Reports
  • Turnitin Scoring Engine (TSE)
  • Errors
  • Introduction

    The Revision Assistant API is based on HTTP and JSON. Rather than many interactions manipulaing granular data it tends toward coarser interactions manipulating larger sets.

    A crucial but separate part of this API is LTI. The primary way instructors and students interact with Revision Assistant is through launching via LTI into an assignment. There is more discussion of LTI below.

    Technically, we use built-in HTTP features, like token-based authentication and HTTP verbs, which can be understood by off-the-shelf HTTP clients. And you should be able to get up and running with the API pretty quickly, no matter what platform you're on. We show examples below using curl commands, as well as snippets of python code, but you shouldn't take that to mean you're restricted to either of these means of access.

    Getting started

    To see if you can reach our servers you can issue a GET against our ping endpoint:

    $ curl https://staging.tiiscoringengine.com/api/ping
    PONG
    

    Next, to check your authentication credentials you can issue a GET to an ping endpoint that will also check your auth. Here's a correct call:

    $ curl -H 'Authorization LTI KEY:SECRET' \
        https://staging.tiiscoringengine.com/api/ping_auth
    {"identifier": "03dcwoeq", "name": "Your District", "created": "2018-04-27T14:52:18Z"}
    

    and here's an incorrect one:

    $ curl -I  -H 'Authorization LTI KEY:BAD_SECRET' \
        https://staging.tiiscoringengine.com/api/ping_auth
    HTTP/1.1 401 UNAUTHORIZED
    Server: openresty
    Date: Wed, 18 Apr 2018 03:26:50 GMT
    ...
    

    To get started you'll need to get access credentials. The most basic credentials are the same ones you'd use to put together an LTI launch, a consumer key and secret. You'll see how to use these in every request below.

    It's worth being explicit that these credentials are different per environment. If you're working against our staging system to exercise your system or tests, you'll have one key and secret. And if you're working against our production system you'll have a different key and secret.

    Swagger documentation

    In addition to this site we provide detailed Swagger documentation for each endpoint. You can get the JSON Swagger data from:

    (This is the only other endpoint for which you don't need authentication credentials.)

    To view it in a UI you can use your favorite Swagger Browser. Or you can plug those URLs into the demo Swagger UI site.

    Be careful! Whatever browser you use should allow you to enter your API Key or LTI credentials, and when you do the calls you make will be live. So you'll probably want to run against staging.

    Authentication

    Setting the 'Authorization' header in python:

    import requests  # great third-party open source HTTP library
    ...
    url = 'https://api.tiiscoringengine.com/api/contexts/ABCD'
    auth_headers = {'Authorization': 'LTI CONSUMER-KEY:CONSUMER-SECRET'}
    response = requests.get(url, headers=auth_headers)
    

    Setting the 'Authorization' header in curl:

    curl \
      -H "Authorization: LTI CONSUMER-KEY:CONSUMER-SECRET" \
      https://api.tiiscoringengine.com/api/contexts/ABCD
    

    Make sure to replace CONSUMER-KEY and CONSUMER-SECRET with your LTI credentials.

    The RA API uses token-based authentication for all interactions. The token used in authentication is part of the setup you'll do with the Product and Engineering staff.

    You'll use that token in a HTTP header. Some of the tokens may be a single string, others may be two strings separated by some delimiter. Whatever that token is it'll be in the Authorization header along with a token type, like these:

    Token types are:

    Whatever form it takes the token must be provided for all requests with a couple explicit exceptions. Requests without a token, or with an invalid token, will result in a 401 Unauthorized response. Requests with a valid token but whose action is not allowed will result in a 403 Forbidden response.

    Additionally, the token defines a scope of operations. For example, an LTI user identifier you define may be unique in your system or userbase but not globally unique. The token constrains the set of data the API has access to for that request.

    LTI

    Students and instructors access Revision Assistant by launching into assignments. These launches match the Learning Tools Interoperability standard (LTI), and to get your users into Revision Assistant you'll need to know how it works. To this end, EduAppCenter and IMS Global Tutorials may be useful overviews. The official specification document is also available but you might want to learn more about some fundamentals before diving into it.

    Additionally, if you're doing this yourself one of the trickiest things to get right is signing the OAuth request. This is a detailed OAuth signing walkthrough that goes through the different steps you need to take to encode your parameters before signing. Revision Assistant uses the "single-part application/x-www-form-urlencoded POST body" form of doing the launch.

    So if you can find a library to create LTI launches for you, do it! Here are a few we've found links for, but we've only tested out the Python library ourselves:

    Terminology

    Data Flow

    An LTI launch into Revision Assistant from your tool will likely look like the following.

    1. A user signs into your tool.
    2. The user navigates through your tool until they reach an assignment.
    3. The user clicks on a button to launch the assignment either in an iframe or in a new window. This button submits a form that issues an LTI launch (POST) request to a Revision Assistant endpoint. The launch request contains information that will be used by Revision Assistant to identify the user and the assignment; if the user has not been seen before it will be created lazily (on-the-fly). The information in the launch request includes (but is not limited to):
      • Context identifier (context_id)
      • Resource link identifier (resource_link_id)
      • User identifier (user_id)

    If the user is an instructor, Revision Assistant allows them to select a prompt to associate with the assignment. After the assignment has been created, further launches list student work progress.

    If the user is a student, Revision Assistant loads the correct assignment into the iframe or new window. The student works on the assignment and submits it when satisfied with their work.

    Also, instead of having Revision Assistant lazily create these objects you can use this API to provision users (teachers and students), contexts, and resource links beforehand, based on workflows in your tool. When doing so you'd provision them with the same identifiers used in the LTI launch so Revision Assistant can match them up. You may also use LTI Names and Roles provisioning specification, discussed below.

    Launch Information

    Launch Point

    Environment LTI Versions URL
    Production 1.1, 1.2 https://api.tiiscoringengine.com/lti/1p0/launch
    Staging 1.1, 1.2 https://staging.tiiscoringengine.com/lti/1p0/launch

    Roles

    The following roles are supported:

    Launch Parameters: Required

    OAuth information:

    LTI protocol information:

    Contextual launch information:

    Additionally, the request must contain one or both of:

    Launch Parameters: Optional

    Custom Launch Parameters

    In addition to the standard LTI parameters we support a set of custom ones. Per the LTI spec these all begin with either custom_ or ext_.

    Assignment Creation

    custom_assignment_instructions

    (Optional) Specify the instructions students will see when they interact with the assignment. Sending these will only write instructions at first assignment launch; changing the instructions on successive launches will not modify them. Instructors launching into the assignment will be able to change the instructions through the UI whether these get sent or not.

    Instructions may contain HTML, but we'll strip out anything more than the most basic formatting before storing it to the database for view by users.

    ext_grader_name

    (Optional) Specify the prompt with which we'll create an assignment rather than having the instructor choose one from the UI. This will not modify the prompt associated with an existing assignment.

    So an Instructor launching into a previously unseen resource link with this parameter will skip the prompt choice screens and go directly into an assignment with the specified prompt. Learners (students) launching into an unseen resource link with this parameter will see an error -- let the Revision Assistant product team know if your workflow should accommodate this.

    Group Information

    custom_parent_group_identifier

    (Optional) As mentioned in the group management docs we provide credentials for you to use in your LTI launch so you can specify where the specified context (class) should sit in the hierarchy -- should it be in one big pool under your account? Under one of your districts? Or an individual school?

    The place in the hierarchy has no effect on the functionalty for students and teachers, but placing the class in the right place can make it easier to roll up usage information.

    You can do this with the consumer key specific to that group, or you can pass in the group identifier using the launch parameter custom_parent_group_identifier. The identifier should be passed along as-is, so if your group (from the List groups API call) has an identifier of "evcwpk4o" then that's exactly what you'd set in the launch parameter.

    User Information

    custom_user_identifiers

    (Optional) Send any number of additional identifiers to associate with this user. When you get user data back from the reporting API we'll include these identifiers so you can correlate them to data in your systems.

    Specify each additional identifier as a key/value pair in the format {key:value}. (This is how identifiers get sent over in OneRoster -- see the userIds field in the users.csv table.) Send multiple identifiers as comma-separated values.

    For example, you'd send a single identifier as:

    {sis:781995}

    and you'd send multiple identifiers as:

    {sis:781995},{uuid:71b7c8f7-1ac9-4a6f-acdf-683c343729d4}

    These will get synced with every launch of that user so you need to send them with every launch.

    Additionally, we support only one identifier per case-insensitive identifier type per user. So if you sent:

    {sis:781995},{SIS:ABC-123}

    we'd only record one of these values under sis.

    Basic Outcomes Service

    Revision Assistant supports the LTI 1.1 Basic Outcomes Service. With this service Revision Assistant can send your system a message whenever a student gets a signal check. And the message can distinguish between clicking the "Signal Check" button and the "Turn In" button.

    To enable the outcomes service you need to provide both of:

    The Outcomes Service provides the means for a score between 0.0 and 1.0. Since Revision Assistant doesn't have percentage based scoring we'll pass back scores of:

    Be aware that Revision Assistant allows students to "Turn In" to an assignment multiple times, and that they can mix and match clicking "Signal Check" and "Turn In" in any order they like.

    A couple more notes:

    1. That the only type of message sent from Revision Assistant is replaceResult.
    2. The message will not be sent synchronously when the student performs a "Signal Check"/"Turn In". In practice the message gets sent within a few seconds of the essay being scored, but it may vary by more than that depending on the amount of system activity.

    See the specification for more details on the message you'll get.

    Context Membership Sync

    For LTI 1.1 launches Revision Assistant supports a basic form of roster (aka, "membership") syncing at the context level. This feature will only work with Instructors, any request to sync a context at launch will be ignored for Learner launches.

    To do this send a URL in the either the custom_context_memberships_url or custom_context_memberships_v2_url launch parameter. The data returned by the URL request should be in either the clunkier LTI 2.0 membership container data format or the more straightforward LTI 1.3 format. (If you're implementing something new it's strongly recommended to use the latter as it will be supported going forward with LTI 1.3+.) Revision Assistant should work with either format.

    When Revision Assistant requests data from the given URL it will use the URL as-is, also adding relevant OAuth information to the request so you can verify the request is from Revision Assistant. The key and secret used to sign the request will be the same ones used in the launch.

    Two final notes:

    1. The URL you send will be called and processed as a background job (asynchronously), so the teacher may not see the students show up immediately in Revision Assistant. (Most of the time they'll see the students very shortly after launching -- usually less than 30 seconds, depending on how responsive your system is and the number of other background jobs happening at the same time.)
    2. You can use this or the API to associate users in bulk with a context. Both talk to the same underlying datastore and use the same logic. The only differences between them are:
      • The API is custom to Revision Assistant so you'd have to do some work to implement it; if you've already implemented the LTI Names and Roles Provisioning specification then there's little reason to use the API. Except...
      • The API works synchronously. And it may be a better experience if an instructor is able to launch into a fully populated roster, no waiting. So it may be worth it if you can easily send a message to this RA API synchronize a context's roster before an instructor launches into it.

    Transactions

    There are a few resources RA provides access to via API. Many of these are composite resources and actions rather than individual data records so we label them as Transactions instead. And some API interactions augment and extend the basic LTI launch paradigm used in Revision Assistant. Some interactions may eventually be more appropriately performed by improvements to the LTI specification -- in particular LTI 1.3 (aka, LTI Advantage) supports more robust roster syncing. Talk with the product team at Revision Assistant about LTI 1.3 support.

    Deleting Resources

    Revision Assistant allows you to delete some resources you've provisioned. You can remove a context, a resource link, and a student. (Details of these are below.) Once successfully invoked we'll mark the resource as deleted and free up the GUID for later use. For students we'll remove any PII associated with the student.

    However, note that unless otherwise noted we will keep the student's writing in the system for the purposes of analysis.

    Transactional examples

    Example creating a new resource link

    curl \
      -d @new_resource_link.json \
      -H "Content-Type: application/json" \
      -H "Authorization: LTI consumerkey:consumersecret" \
      https://staging.tiiscoringengine.com/api/resource-links
    

    Note that the examples use the staging environment (staging.tiiscoringengine.com) rather than the production environment (api.tiiscoringengine.com) -- you can adjust as your needs dictate.

    Below we provide a number of examples of the API requests and responses, trying to use the same set of identifiers and data to build up a simple story.

    As discussed in authentication we support multiple types of authentication headers. However, examples below will be using the LTI consumer key/secret authorization. Each example also typically has a content type and data to send that we'll reference as a file.

    Create a context and sync its roster

    For example, say we want to create a new class "Batman Studies" in the production RA environment, directly underneath your tenant -- return value is the same as "View a Context's Details", below:

    # new_class.json
    {
      "context_id": "d748ab58",
      "context_title": "Batman Studies"
    }
    
    curl \
      -d @new_class.json \
      -H "Content-Type: application/json" \
      -H "Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/contexts
    

    You can also include one or more instructors:

    # new_class_with_instructors.json
    {
      "context_id": "d748ab58",
      "context_title": "Batman Studies",
      "instructors": [{
          "user_id": "0021ce40",
          "first_name": "Alfred",
          "last_name": "Pennyworth"
        }, {
          "user_id": "85fec2e9",
          "first_name": "Bruce",
          "last_name": "Wayne"
      }]
    }
    
    curl \
      -d @new_class_with_instructors.json \
      -H "Content-Type: application/json" \
      -H "Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/contexts
    

    Later, we want to add two students:

    # new_students_1.json
    {
      "context_id": "d748ab58",
      "context_title": "Batman Studies",
      "students": [{
          "user_id": "6bf6b34b",
          "first_name": "Richard",
          "last_name": "Grayson",
          "additional_identifiers": [
              {"identifier_type": "sis", "identifier": "811556dc-8882-4b06-9998-a52223481aa7"}
          ]
        }, {
          "user_id": "844115d1",
          "first_name": "Damian",
          "last_name": "Wayne",
          "additional_identifiers": [
              {"identifier_type": "sis", "identifier": "f1478516-14a6-4304-9931-aa884af74eeb"}
          ]
      }]
    }
    
    curl \
      -d @new_students_1.json \
      -H "Content-Type: application/json" \
      -H "Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/contexts
    

    And then later one more, though we send the whole roster:

    # new_students_2.json
    {
      "context_id": "d748ab58",
      "context_title": "Batman Studies",
      "students": [{
          "user_id": "6bf6b34b",
          "first_name": "Richard",
          "last_name": "Grayson",
          "additional_identifiers": [
              {"identifier_type": "sis", "identifier": "811556dc-8882-4b06-9998-a52223481aa7"}
          ]
        }, {
          "user_id": "4acb4db8",
          "first_name": "Leeloo",
          "last_name": "Multipass",
          "additional_identifiers": [
              {"identifier_type": "sis", "identifier": "001709a2-3e9f-4fdd-82fd-a0167539e011"}
          ]
        }, {
          "user_id": "844115d1",
          "first_name": "Damian",
          "last_name": "Wayne",
          "additional_identifiers": [
              {"identifier_type": "sis", "identifier": "f1478516-14a6-4304-9931-aa884af74eeb"}
          ]
      }]
    }
    
    curl \
      -d @new_students_2.json \
      -H "Content-Type: application/json" \
      -H Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/contexts
    

    Alternatively, you can specify a parent under which to provision this class:

    # new_class_with_parent.json
    {
      "context_id": "d748ab58",
      "context_title": "Batman Studies",
      "parent_group_identifier": "evcwpk3w"
    }
    
    curl \
      -d @new_class_with_parent.json \
      -H "Content-Type: application/json" \
      -H "Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/contexts
    

    Path: POST /api/contexts

    This will lazily create:

    You can use this to:

    Data to provision a class are:

    Notes:

    The response body is the same as GET /api/contexts/{context-guid}.

    View a Context's Details

    Let's get the current state of our Batman class:

    curl \
      -H "Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/contexts/d748ab58
    
    {
      "context_id": "d748ab58",
      "context_title": "Batman Studies",
      "created": "2018-02-19T14:32:19Z",
      "state": "open",
      "group_identifier": "xkpc7m3z",
      "parent_group_identifier": "y9ocq39y",
      "instructors": [{
          "created": "2018-02-19T14:32:19Z",
          "user_id": "0021ce40",
          "first_name": "Alfred",
          "last_name": "Pennyworth"
        }, {
          "created": "2018-02-19T14:32:19Z",
          "user_id": "85fec2e9",
          "first_name": "Bruce",
          "last_name": "Wayne"
      }],
      "students": [{
          "created": "2018-02-19T14:32:19Z",
          "user_id": "6bf6b34b",
          "first_name": "Richard",
          "last_name": "Grayson",
          "additional_identifiers": [
              {"identifier_type": "sis", "identifier": "811556dc-8882-4b06-9998-a52223481aa7"}
        }, {
          "created": "2018-02-19T18:52:41Z",
          "user_id": "4acb4db8",
          "first_name": "Leeloo",
          "last_name": "Multipass",
          "additional_identifiers": [
              {"identifier_type": "sis", "identifier": "001709a2-3e9f-4fdd-82fd-a0167539e011"}
        }, {
          "created": "2018-02-19T14:32:19Z",
          "user_id": "844115d1",
          "first_name": "Damian",
          "last_name": "Wayne",
          "additional_identifiers": [
              {"identifier_type": "sis", "identifier": "f1478516-14a6-4304-9931-aa884af74eeb"}
      }]
    }
    

    Path: GET /api/contexts/:urlencoded-context-id

    This will return the current data of the context, including its state, name, identifier, instructor(s), and student roster.

    Notes:

    Rename a Context ID

    Change the context ID:

    # new_context_id.json
    {"new_context_id": "c78d744b"}
    
    curl \
      -H "Content-Type: application/json" \
      -H "Authorization: LTI mykey:mysecret" \
      -d @new_context_id.json
      https://staging.tiiscoringengine.com/api/contexts/d748ab58/rename
    

    Path: POST /api/contexts/:urlencoded-context-id/rename

    If the identifier you pass to RA for the context changes in your system you can rename it in RA as well. (Generally you should strive to choose identifiers so this won't be necessary, but things happen.)

    This change takes effect immediately. Users currently in the system will not be affected, but instructors launching after this change with the old context ID will create a new record.

    If successful this will return a 204 No Content.

    If no context exists with the context ID in the URL you'll get a 404 Not Found.

    If a context already exists with the given new_context_id you'll get a 409 Conflict.

    Archive/Unarchive a Context

    Path (archive): POST /api/contexts/:urlencoded-context-id/archive

    This will mark the specified context as archived, along with all of its associated resource links. Students and teachers can still launch into archived resource links (assignments) if your application supports that, but they cannot make any changes -- no new drafts, no changed instructions, nothing.

    There is no POST body associated with this request.

    If successful this will return a 204 No Content.

    If the given context does not exist within your consumer this will return a 404 Not Found.

    Path (unarchive): POST /api/contexts/:urlencoded-context-id/unarchive

    This will mark the specified context as open. Unlike the archiving call it will not make any changes to associated resource links, you'll need to do that yourself for each resource link you want to change.

    There is no POST body associated with this request.

    If successful this will return a 204 No Content.

    If the given context does not exist within your consumer this will return a 404 Not Found.

    Delete a Context

    Path: DELETE /api/contexts/:urlencoded-context-id

    This will mark the specified the context as deleted, along with any of its associated resource links. This will also free up the GUIDs associated with each so you can re-use if desired.

    Note that the students associated with the context and resource links will remain in the system, as will the essays the students wrote.

    NOTE This operation cannot be undone

    If successful this will return a 204 No Content.

    If the given context does not exist within your consumer this will return a 404 Not Found.

    Summarize Resource Links in Contexts

    Fetch the resource links and summary info for a single context

    curl \
      -H "Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/contexts/resource-link-summaries?context_id=d748ab58
    
    {
      "contexts": [
        {
          "context_id": "d748ab58",
          "context_title": "Period 1: Superman Studies",
          "group_identifier": "051cv15g",
          "parent_group_identifier": "xoecd6oy",
          "created": "2018-05-10T19:41:59Z",
          "state": "open",
          "resource_links": [
            {
              "resource_link_config": {
                  "instructions": "Set aside your <b>feelings</b> about the villain",
                  "prompt_slug": "whats_wrong_with_lex_luthor_analysis_1_0_0",
                  "title": "Lex Luthor should be president"
              },
              "resource_link_id": "3f177a0e",
              "resource_link_state": "open",
              "resource_link_activity": {
                  "students_with_autosave": 5,
                  "students_with_prewriting": 3,
                  "students_with_drafts": 5,
                  "students_with_submissions": 3
              }
            },
            {
              "resource_link_config": {
                  "instructions": "Analyze the best use of this superpower",
                  "prompt_slug": "whats_right_with_green_lantern_1_0_0",
                  "title": "Green Lantern: Thoughts vs Reality"
              },
              "resource_link_id": "60fb68b9",
              "resource_link_state": "closed",
              "resource_link_activity": {
                  "students_with_autosave": 21,
                  "students_with_prewriting": 4,
                  "students_with_drafts": 21,
                  "students_with_submissions": 19
              }
            }
          ]
        }
      ]
    }
    

    Fetch the resource links and summary info for a multiple contexts, one with no assignments

    curl \
      -H "Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/contexts/resource-link-summaries?context_id=c78d744b&context_id=d748ab58&context_id=74540cae
    
    {
      "contexts": [
        {
          "context_id": "d748ab58",
          "context_title": "Period 1: Superman Studies",
          "group_identifier": "051cv15g",
          "parent_group_identifier": "xoecd6oy",
          "created": "2018-05-10T19:41:59Z",
          "state": "open",
          "resource_links": [
            {
              "resource_link_config": {
                  "instructions": "Set aside your <b>feelings</b> about the villain",
                  "prompt_slug": "whats_wrong_with_lex_luthor_analysis_1_0_0",
                  "title": "Lex Luthor should be president"
              },
              "resource_link_id": "3f177a0e",
              "resource_link_state": "open",
              "resource_link_activity": {
                  "students_with_autosave": 21,
                  "students_with_prewriting": 4,
                  "students_with_drafts": 21,
                  "students_with_submissions": 19
              }
            },
            {
              "resource_link_config": {
                  "instructions": "Analyze the best use of this superpower",
                  "prompt_slug": "whats_right_with_green_lantern_1_0_0",
                  "title": "Green Lantern: Thoughts vs Reality"
              },
              "resource_link_id": "60fb68b9",
              "resource_link_state": "closed",
              "resource_link_activity": {
                  "students_with_autosave": 5,
                  "students_with_prewriting": 3,
                  "students_with_drafts": 5,
                  "students_with_submissions": 3
              }
            },
          ]
        },
        {
          "context_id": "74540cae",
          "context_title": "Period 2: Superman Studies",
          "group_identifier": "f68ffa88",
          "parent_group_identifier": "xoecd6oy",
          "created": "2018-05-12T20:08:41Z",
          "state": "open",
          "resource_links": [
            {
              "resource_link_config": {
                  "instructions": "Set aside your <b>feelings</b> about the villain",
                  "prompt_slug": "whats_wrong_with_lex_luthor_analysis_1_0_0",
                  "title": "Lex Luthor should be president"
              },
              "resource_link_id": "1d7ac8b8",
              "resource_link_state": "closed",
              "resource_link_activity": {
                  "students_with_autosave": 5,
                  "students_with_prewriting": 3,
                  "students_with_drafts": 5,
                  "students_with_submissions": 3
              }
            },
            {
              "resource_link_config": {
                  "instructions": "Analyze the best use of this superpower",
                  "prompt_slug": "whats_right_with_green_lantern_1_0_0",
                  "title": "Green Lantern: Thoughts vs Reality"
              },
              "resource_link_id": "a9f822c6",
              "resource_link_state": "open",
              "resource_link_activity": {
                  "students_with_autosave": 21,
                  "students_with_prewriting": 4,
                  "students_with_drafts": 21,
                  "students_with_submissions": 19
              }
            },
          ]
        },
        {
          "context_id": "c78d744b",
          "context_title": "Period 3: Superman Studies",
          "group_identifier": "b56cb680",
          "parent_group_identifier": "xoecd6oy",
          "created": "2018-05-12T20:08:41Z",
          "state": "open",
          "resource_links": []
        }
      ]
    }
    

    Path: GET /api/contexts/resource-link-summaries

    This transaction lets you view summary information about the students doing different types of work in the resource links associated with one of a set of contexts. Much of the information is the same as you've seen before around the context and resource link. But each resource link includes a new item, resource_link_activity, that shows you the number of students doing:

    You may also pass in a query arg include_activity=false to not include the resource_link_activity information. If you don't need that information it'll speed up the response time by quite a bit.

    Create a Resource Link and Context

    First, we're going to add an assignment to our Batman Studies class. Since we already have our student roster synchronized we won't send any roster information. Also, note that because there is only one resource link provided we'll return it inline rather than in an array:

    # assignment_1.json
    {
      "context_id": "c78d744b",
      "context_title": "Batman Studies",
      "resource_link_id": "6d74da8f",
      "resource_link_config": {
        "prompt_slug": "whats_wrong_with_the_joker_analysis_1_0_0",
        "title": "The Joker: Menace or Freedom Fighter?"
      },
      "instructors": [{
          "user_id": "f4c3b9bd",
          "first_name": "Clark",
          "last_name": "Kent"
      }],
      "students": []
    }
    
    curl \
      -d @assignment_1.json \
      -H "Content-Type: application/json" \
      -H "Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/resource-links
    

    Second example with new class and roster, plus assignment with instructions and a full response -- note that because you gave us one resource link we'll return it inline rather than in an array:

    # assignment_2.json
    {
      "context_id": "6ccd9403-ec19-4735-bfc6-99a2cd640b16",
      "context_title": "Superman Studies",
      "resource_link_id": "6bd21261-fca6-4b84-af8d-787444ef1d8d6",
      "resource_link_config": {
        "instructions": "Set aside your <b>feelings</b> about the villain",
        "prompt_slug": "whats_wrong_with_lex_luthor_analysis_1_0_0",
        "title": "Lex Luthor should be president"
      },
      "instructors": [{
          "user_id": "f4c3b9bd",
          "first_name": "Clark",
          "last_name": "Kent"
      }],
      "students": [{
          "user_id": "60a9ac41",
          "first_name": "Corben",
          "last_name": "Dallas",
          "additional_identifiers": [
              {"identifier_type": "sis", "identifier": "0138a001-a700-47b2-a7d9-965168cb278c"}
          ]
        }, {
          "user_id": "4acb4db8",
          "first_name": "Leeloo",
          "last_name": "Multipass",
          "additional_identifiers": [
              {"identifier_type": "sis", "identifier": "001709a2-3e9f-4fdd-82fd-a0167539e011"}
          ]
        }, {
          "user_id": "451180c7",
          "first_name": "Diana",
          "last_name": "Prince",
          "additional_identifiers": [
              {"identifier_type": "sis", "identifier": "7b6c3500-9321-4b97-b71d-327ff7536df8"}
          ]
        }, {
          "user_id": "6bf6b34b",
          "first_name": "Bruce",
          "last_name": "Wayne",
          "additional_identifiers": [
              {"identifier_type": "sis", "identifier": "98f8a5f7-3c3d-4233-82d7-f236ba49e1a0"}
          ]
      }]
    }
    
    curl \
      -d @assignment_2.json \
      -H "Content-Type: application/json" \
      -H "Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/resource-links
    

    Response:

    {
      "context_id": "6ccd9403-ec19-4735-bfc6-99a2cd640b16",
      "context_title": "Superman Studies",
      "group_identifier": "051cv15g",
      "parent_group_identifier": "xoecd6oy",
      "created": "2018-05-10T19:41:59Z",
      "instructors": [
        {
          "additional_identifiers": [],
          "created": "2018-05-10T19:41:59Z",
          "display_name": "Clark Kent",
          "first_name": "Clark",
          "id": 667736,
          "identity_id": 1086976,
          "last_name": "Kent",
          "user_id": "f4c3b9bd"
        }
      ],
      "resource_link_config": {
        "instructions": "Set aside your <b>feelings</b> about the villain",
        "prompt_slug": "whats_wrong_with_lex_luthor_analysis_1_0_0",
        "title": "Lex Luthor should be president"
      },
      "resource_link_id": "6bd21261-fca6-4b84-af8d-787444ef1d8d6",
      "resource_link_state": "open",
      "students": [{
          "additional_identifiers": [{
            "identifier": "0138a001-a700-47b2-a7d9-965168cb278c",
            "identifier_type": "sis"
          }],
          "created": "2018-05-10T19:41:59Z",
          "display_name": "Corben Dallas",
          "first_name": "Corben",
          "id": 667737,
          "identity_id": 1086977,
          "last_name": "Dallas",
          "user_id": "60a9ac41"
        },
        {
          "additional_identifiers": [{
            "identifier": "001709a2-3e9f-4fdd-82fd-a0167539e011",
            "identifier_type": "sis"
          }],
          "created": "2018-05-10T19:41:59Z",
          "display_name": "Leeloo Multipass",
          "first_name": "Leeloo",
          "id": 667738,
          "identity_id": 1086978,
          "last_name": "Multipass",
          "user_id": "4acb4db8"
        },
        {
          "additional_identifiers": [{
            "identifier": "7b6c3500-9321-4b97-b71d-327ff7536df8",
            "identifier_type": "sis"
          }],
          "created": "2018-05-10T19:41:59Z",
          "display_name": "Diana Prince",
          "first_name": "Diana",
          "id": 667739,
          "identity_id": 1086979,
          "last_name": "Prince",
          "user_id": "451180c7"
        },
        {
          "additional_identifiers": [{
            "identifier": "98f8a5f7-3c3d-4233-82d7-f236ba49e1a0",
            "identifier_type": "sis"
          }],
          "created": "2018-05-10T19:41:59Z",
          "display_name": "Bruce Wayne",
          "first_name": "Bruce",
          "id": 667740,
          "identity_id": 1086980,
          "last_name": "Wayne",
          "user_id": "6bf6b34b"
        }
      ]
    }
    

    Third example adding multiple resource links to an existing context -- you can also use this to create multiple resource link with a new context from the get-go. Note that we return the resource links in an array rather than inline.

    # assignment_3.json
    {
      "context_id": "19a3395e-c89b-4c1f-b3a3-2a16efdaaf98",
      "resource_links": [
        {
          "resource_link_id": "a3a53a3a-5353-4039-b5e6-3313dc23c57e",
          "resource_link_config": {
            "instructions": "Talk about motivation",
            "prompt_slug": "why_does_loki_do_loki_analysis_1_0_0",
            "title": "Why does Loki do Loki?",
            "capabilities": {
              "prewriting": false
            }
          }
        },
        {
          "resource_link_id": "78354690-c2c5-4375-969a-b981037e690c",
          "resource_link_config": {
            "instructions": "Tell a story where your past actions have informed your future self",
            "prompt_slug": "past_and_future_narrative_1_0_0",
            "title": "Integrating the past with the future",
            "capabilities": {
              "prewriting": false,
              "draft_history": false
            }
          }
        }
      ]
    }
    
    curl \
      -d @assignment_3.json \
      -H "Content-Type: application/json" \
      -H "Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/resource-links
    

    Response:

    {
      "context_id": "19a3395e-c89b-4c1f-b3a3-2a16efdaaf98",
      "context_title": "Green Lantern Studies",
      "group_identifier": "qwcz5m1b",
      "parent_group_identifier": "y8mc8589",
      "created": "2021-09-05T07:22:34Z",
      "resource_links": [
        {
          "resource_link_id": "a3a53a3a-5353-4039-b5e6-3313dc23c57e",
          "resource_link_config": {
            "instructions": "Talk about motivation",
            "prompt_slug": "why_does_loki_do_loki_analysis_1_0_0",
            "title": "Why does Loki do Loki?",
            "capabilities": {
              "draft_history": true,
              "holistic_feedback": true,
              "inline_feedback": true,
              "prewriting": false,
              "proofread": true
            }
          },
          "resource_link_state": "open"
        },
        {
          "resource_link_id": "78354690-c2c5-4375-969a-b981037e690c",
          "resource_link_config": {
            "instructions": "Tell a story where your past actions have informed your future self",
            "prompt_slug": "past_and_future_narrative_1_0_0",
            "title": "Integrating the past with the future",
            "capabilities": {
              "draft_history": false
              "holistic_feedback": true,
              "inline_feedback": true,
              "prewriting": false,
              "proofread": true
            }
          },
          "resource_link_state": "open"
        }
      ],
      ...
    }
    

    Path: POST /api/resource-links

    NOTE: This is primarily supported for use cases where teachers are not expected to launch into an assignment before students. If your use case supports that you will not need to exercise this endpoint.

    This will lazily create:

    This request data is very close to creating a new context. The only additional fields are:

    In the second example. we add a brand new class, and roster, along with our assignment, which in this case includes instructions that Revision Assistant will display to students.

    Note that the teacher is the same, as are a couple of the students from the Batman Studies class -- these existing students will not be re-created, but they will be assigned to the new class.

    Also, just as in the context creation transaction you can provide a parent_group_identifier under which to provision the context. See the group management API for more.

    Notes:

    Capabilities

    Disable a single capability so students do not see prewriting

    {
      ...
      "resource_link_id": "6bd21261-fca6-4b84-af8d-787444ef1d8d6",
      "resource_link_config": {
        "instructions": "Set aside your <b>feelings</b> about the villain",
        "prompt_slug": "whats_wrong_with_lex_luthor_analysis_1_0_0",
        "title": "Lex Luthor should be president",
        "capabilities": {
          "prewriting": false
        }
      },
      ...
    

    Disabling multiple capabilities so students cannot see draft history, whole-essay feedback, sentence-level feedback, or go into proofread mode.

    {
      ...
      "resource_link_id": "6bd21261-fca6-4b84-af8d-787444ef1d8d6",
      "resource_link_config": {
        "instructions": "Set aside your <b>feelings</b> about the villain",
        "prompt_slug": "whats_wrong_with_lex_luthor_analysis_1_0_0",
        "title": "Lex Luthor should be president",
        "capabilities": {
          "draft_history": false,
          "holistic_feedback": false,
          "inline_feedback": false,
          "proofread": false
        }
      },
      ...
    

    Every type of assignment comes with a set of capabilities, which generally map to user-visible features. Some of them can be disabled when you create the resource link. You do so in the resource_link_config.capabilities set of fields.

    The available capabilities are:

    The capabilities that exist by default for different assignment types are:

    If you say you want to disable a capability that doesn't already exist in that assignment type it'll be a no-op.

    One more example: say you wanted students in a signal check assignment to not get any feedback, see a draft history, or go into proofread mode. See the 'Disabling multiple capabilities' example for what you'd send.

    Note that while you do pass in a flag for each capability we only support false; passing in null true will be a no-op. (As noted above teacher_scores is the exception.) Future improvements may allow you to enable capabilities not supported by default for an assignment type.

    Finally: these are fairly powerful knobs to turn, and you should be sure the resulting behavior is what you want. Test them out on a QA/Staging account and do manual verification to make sure.

    View a Resource Link's Details

    Path: GET /api/contexts/:urlencoded-context-id/resource-links/:urlencoded-resource-link-id

    This lists the current details of a resource link, returning the same data as when it's created the first time.

    List Resource Links in a Context

    Path: GET /api/contexts/:urlencoded-context-id/resource-links

    This lists all resource links associated with a context. It returns the basic data for a context, the instructors associated with the context, and the standard information for each resource link, regardless of that resource link's state. It does not include student memberships, and it does not include any work- or status-related data.

    Get Current Status for Resource Link Work

    First we'll get the status for a single student in a resource link:

    curl \
      -H "Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/contexts/ABCD/resource-links/EFGH/work-status/6bf6b34b
    
    {
      "context_id": "ABCD",
      "resource_link_id": "EFGH",
      "user_id": "6bf6b34b",
      "autosave": true,
      "prewriting": false,
      "signal_check": 18,
      "submission": 3,
      "latest_draft_time": "2019-03-20T14:39:12Z",
      "latest_submission_time": "2019-03-20T14:39:12Z",
      "latest_scores": {
          "Analysis": 2,
          "Claim": 2,
          "Language": 3,
          "Organization": 3
      }
    }
    

    Next, we'll get status for all students in the class -- note that the third student hasn't done any work and the second student has not yet submitted:

    curl \
      -H "Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/contexts/ABCD/resource-links/EFGH/work-status
    
    {
      "results": [{
        "context_id": "ABCD",
        "resource_link_id": "EFGH",
        "user_id": "6bf6b34b",
        "autosave": true,
        "prewriting": false,
        "signal_check": 18,
        "submission": 3,
        "latest_draft_time": "2019-03-20T14:39:12Z",
        "latest_submission_time": "2019-03-20T14:39:12Z",
        "latest_scores": {
            "Analysis": 2,
            "Claim": 2,
            "Language": 3,
            "Organization": 3
        }
      }, {
        "context_id": "ABCD",
        "resource_link_id": "EFGH",
        "user_id": "4acb4db8",
        "autosave": true,
        "prewriting": true,
        "signal_check": 1,
        "submission": 0,
        "latest_draft_time": "2019-03-20T14:39:12Z",
        "latest_submission_time": "",
        "latest_scores": {
            "Analysis": 2,
            "Claim": 2,
            "Language": 1,
            "Organization": 2
        }
      }, {
        "context_id": "ABCD",
        "resource_link_id": "EFGH",
        "user_id": "844115d1",
        "autosave": false,
        "prewriting": false,
        "signal_check": 0,
        "submission": 0,
        "latest_draft_time": "",
        "latest_submission_time": "",
        "latest_scores": {}
      }]
    }
    

    Finally, we'll get status for all active resource links that a single student is in. Note that the third one has no activity.

    curl \
      -H "Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/users/6bf6b34b/work-status
    
    {
      "results": [{
        "context_id": "ABCD",
        "resource_link_id": "EFGH",
        "user_id": "6bf6b34b",
        "autosave": true,
        "prewriting": false,
        "signal_check": 18,
        "submission": 3,
        "latest_draft_time": "2019-03-20T14:39:12Z",
        "latest_submission_time": "2019-03-20T14:39:12Z",
        "latest_scores": {
            "Analysis": 4,
            "Claim": 3,
            "Language": 4,
            "Organization": 3
        }
      }, {
        "context_id": "LMNO",
        "resource_link_id": "PQRS",
        "user_id": "6bf6b34b",
        "autosave": true,
        "prewriting": true,
        "signal_check": 1,
        "submission": 0,
        "latest_draft_time": "2019-03-20T14:39:12Z",
        "latest_submission_time": "",
        "latest_scores": {
            "Analysis": 2,
            "Claim": 2,
            "Language": 1,
            "Organization": 2
        }
      }, {
        "context_id": "LMNO",
        "resource_link_id": "TUVW",
        "user_id": "6bf6b34b",
        "autosave": false,
        "prewriting": false,
        "signal_check": 0,
        "submission": 0,
        "latest_draft_time": "",
        "latest_submission_time": "",
        "latest_scores": {}
      }]
    }
    

    Path: GET /api/contexts/:urlencoded-context-id/resource-links/:urlencoded-resource-link-id/work-status/:urlencoded-student-user-id

    Path: GET /api/contexts/:urlencoded-context-id/resource-links/:urlencoded-resource-link-id/work-status

    Path: GET /api/users/:urlencoded-student-user-id/work-status

    This quick report gives you the status of:

    Unlike reporting it is real-time, so if the student submitted a draft seconds before you call this resource it will be represented in the response.

    You'll get a 404 Not Found if no context exists with the context ID in the URL, or if no resource link exists within that context using the resource link ID in the URL. You'll also get a 404 if you ask for the status of a particular student and we have no record of that student anywhere in the system.

    However, if you request the status for a particular student and we do have a record for the student but do not have any record of them doing work for the assignment we'll return an empty work status record (where fields are either false, 0, or an empty string) rather than a 404. The reasoning for the difference is that we just may not have seen the requested student launch into the context (class) and assignment (resource link) yet, and we assume that if you're requesting the status you have some reason to believe that the student may eventually do work in the class and assignment.

    The meaning of each field:

    Note that the timestamps, similar to those in reports, are UTC and in ISO 8601 format

    Rename a Resource Link ID

    Change the resource link ID:

    # new_resource_link_id.json
    {"new_resource_link_id": "b63c9257"}
    
    curl \
      -H "Authorization: LTI mykey:mysecret" \
      -H "Content-Type: application/json" \
      -d @new_resource_link_id.json
      https://staging.tiiscoringengine.com/api/contexts/c78d744b/resource-links/6d74da8f/rename
    

    Path: POST /api/contexts/:urlencoded-context-id/resource-links/:urlencoded-resource-link-id/rename

    If the identifier you pass to RA for the resource link changes in your system you can rename it in RA as well. (Generally you should strive to choose identifiers so this won't be necessary, but things happen.)

    This change takes effect immediately. Users currently in the system will not be affected, but instructors launching after this change with the old resource link ID will be presented with the 'Choose A Prompt' library page.

    If successful this will return a 204 No Content.

    If no context exists with the context ID in the URL, or if no resource link exists within that context using the resource link ID in the URL, you'll get a 404 Not Found.

    If a resource link already exists with the given new_resource_link_id you'll get a 409 Conflict.

    Update a Resource Link

    Change the state of the assignment associated with the resource link:

    # resource_link_update.json
    {"state": "closed"}
    
    curl \
      -H "Authorization: LTI mykey:mysecret" \
      -H "Content-Type: application/json" \
      -d @resource_link_update.json
      https://staging.tiiscoringengine.com/api/contexts/c78d744b/resource-links/6d74da8f
    

    Update the instructions and title of the assignment associated with the resource link, disable one capability and enable another:

    # resource_link_update.json
    {
      "instructions": "<p>These are <b>the most important</b> instructions ever.</p>",
      "title": "My new title -- it's the best!",
      "capabilities": {
        "inline_feedback": false,
        "proofread": true
      }
    }
    
    curl \
      -H "Authorization: LTI mykey:mysecret" \
      -H "Content-Type: application/json" \
      -d @resource_link_update.json
      https://staging.tiiscoringengine.com/api/contexts/c78d744b/resource-links/6d74da8f
    

    Path: POST /api/contexts/:urlencoded-context-id/resource-links/:urlencoded-resource-link-id

    With this transaction you can update basic information about an assignment. Updateable attributes are:

    Updates take effect immediately, but they will not affect students or teachers currently working in the assignment -- they'll need to re-launch into the assignment to see the updates or their effects. (And a full re-launch is required, not just refreshing the page.)

    If successful this will return a 200 and the current state of the resource link (same data as fetching its current state).

    If the update payload is invalid you'll get a 400 and an error document describing the problem.

    You'll also get a 400 and a message if you try to update a resource link that hasn't been fully configured yet -- that is, not associated with a prompt.

    Finally, if either the context or resource link specified in the URL aren't available in your consumer you'll get a 404 Not Found.

    Delete a Resource Link

    Path: DELETE /api/contexts/:urlencoded-context-id/resource-links/:urlencoded-resource-link-id

    This will mark the specified resource link as deleted. This will also free up the GUID associated with it so you can re-use if desired.

    The students associated with the resource link remain in the system, and the essays the students wrote will also remain for the purpooses of analysis.

    NOTE This operation cannot be undone

    This will return a 404 Not Found if the given context or resource link does not exist within your consumer, or if the specified resource link does not exist within the given context.

    Transfer a Student Between Contexts

    Path: POST /api/contexts/actions/transfer

    Move a student and his/her work from one context to another

    # transfer_1.json
    {
      "user_id": "60a9ac41",
      "source_context_id": "6ccd9403-ec19-4735-bfc6-99a2cd640b16",
      "target_context_id": "2f48e982-49c5-40d3-8163-ae99c4da46dd",
      "policy": "best effort"
    }
    
    curl \
      -d @transfer_1.json \
      -H "Content-Type: application/json" \
      -H "Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/contexts/actions/transfer
    

    Response 1, some assignments moved but not all -- note that we do not end the membership of the student to the source class because there is still work remaining:

    {
      "user_id": "60a9ac41",
      "source_context_id": "6ccd9403-ec19-4735-bfc6-99a2cd640b16",
      "target_context_id": "2f48e982-49c5-40d3-8163-ae99c4da46dd",
      "policy": "best effort",
      "results": {
        "message": "OK",
        "status": "some",
        "source_membership_status": "no action",
        "target_membership_status": "created",
        "resource_link_status": [{
          "source_resource_link_id": "6bd21261-fca6-4b84-af8d-787444ef1d8d6",
          "target_resource_link_id": "4af99f25-0f44-44b2-99e6-d45c68697875f",
          "prompt_slug": "whats_wrong_with_the_joker_analysis_1_0_0",
          "status": "moved"
        }, {
          "source_resource_link_id": "e5073d5b-f8ae-4fae-a5d8-59d8a140add7",
          "target_resource_link_id": "b54b2af6-8bec-4ffe-bba5-e0e060bf210f",
          "prompt_slug": "who_knows_about_atoms_analysis_1_0_0",
          "status": "moved"
        }, {
          "source_resource_link_id": "7898fc81-3a98-4eb4-85ca-54150b163f44",
          "target_resource_link_id": null,
          "prompt_slug": "understanding_gramdma_narrative_3_0_0",
          "status": "not matched"
        }]
      }
    }
    

    Response 2, data not found:

    {
      "user_id": "60a9ac41",
      "source_context_id": "6ccd9403-ec19-4735-bfc6-99a2cd640b16",
      "target_context_id": "2f48e982-49c5-40d3-8163-ae99c4da46dd",
      "policy": "best effort",
      "results": {
        "message": "User not found"
        "status": "fail",
        "source_membership_status": "no action",
        "target_membership_status": "no action",
        "resource_link_status": []
      }
    }
    

    Response 3, student already belongs to target and request specifies this is not allowed:

    # transfer_1.json
    {
      "user_id": "60a9ac41",
      "source_context_id": "6ccd9403-ec19-4735-bfc6-99a2cd640b16",
      "target_context_id": "2f48e982-49c5-40d3-8163-ae99c4da46dd",
      "policy": "best effort",
      "can_belong_to_target": false
    }
    
    {
      "user_id": "60a9ac41",
      "source_context_id": "6ccd9403-ec19-4735-bfc6-99a2cd640b16",
      "target_context_id": "2f48e982-49c5-40d3-8163-ae99c4da46dd",
      "policy": "best effort",
      "results": {
        "message": "User already belongs to target context",
        "status": "fail",
        "source_membership_status": "no action",
        "target_membership_status": "no action",
        "resource_link_status": []
      }
    }
    

    Response 4, all assignments moved

    {
      "user_id": "60a9ac41",
      "source_context_id": "6ccd9403-ec19-4735-bfc6-99a2cd640b16",
      "target_context_id": "2f48e982-49c5-40d3-8163-ae99c4da46dd",
      "policy": "best effort",
      "results": {
        "message": "OK",
        "status": "all",
        "source_membership_status": "ended",
        "target_membership_status": "created",
        "resource_link_status": [{
          "source_resource_link_id": "6bd21261-fca6-4b84-af8d-787444ef1d8d6",
          "target_resource_link_id": "4af99f25-0f44-44b2-99e6-d45c68697875f",
          "prompt_slug": "whats_wrong_with_the_joker_analysis_1_0_0",
          "status": "moved"
        ]}
      }
    }
    

    Response 5, no assignments moved because of policy (change "policy" to "all or none" in original request)

    {
      "user_id": "60a9ac41",
      "source_context_id": "6ccd9403-ec19-4735-bfc6-99a2cd640b16",
      "target_context_id": "2f48e982-49c5-40d3-8163-ae99c4da46dd",
      "policy": "all or none",
      "results": {
        "message": "OK",
        "status": "halt",
        "source_membership_status": "no action",
        "target_membership_status": "no action",
        "resource_link_status": [{
          "source_resource_link_id": "6bd21261-fca6-4b84-af8d-787444ef1d8d6",
          "target_resource_link_id": null,
          "prompt_slug": "whats_wrong_with_the_joker_analysis_1_0_0",
          "status": "not matched"
        }, {
          "source_resource_link_id": "e5073d5b-f8ae-4fae-a5d8-59d8a140add7",
          "target_resource_link_id": "b54b2af6-8bec-4ffe-bba5-e0e060bf210f",
          "prompt_slug": "who_knows_about_atoms_analysis_1_0_0",
          "status": "matched"
        ]}
      }
    }
    

    This will move a student and their work from one class to another. Note that this is not required for day-to-day use of Revision Assistant -- a student can launch into multiple classes with no extra configuration or effort, and as long as they're launched with unique context IDs Revision Assistant will keep the data separate.

    But it can happen that a student joins the wrong class and does work within that class. If you want to move the student to a correct class and have their work move with them, this API call is for you.

    The data we need are pretty simple:

    We use the prompt associated with the assignment when we try to match the assignment from the source context to the target. In particular, the prompt_slug property must be the same.

    In response you'll get the same data echoed back, plus a "results" map with the following:

    Note that if you use "ANY" for the source_context_id the above response structure will come back for each transfer attempted, all under a top-level "transactions" key.

    View a Student's Answer History

    To get all a student's answers for a single resource link::

    curl \
      -H "Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/contexts/ABCD/resource-links/EFGH/answers/6bf6b34b
    
    {
      "answers": [
        {
            "id": 1234,
            "created": "2019-03-20T14:57:42Z",
            "title": "The answer title",
            "html": "<p>The student's answer text</p>",
            "reviewer_comment": null,
            "author_comment": null,
            "is_submission": true,
            "answer_type": "signal_check",
            "signal_check_scores": {
                "Analysis": 1,
                "Claim": 2,
                "Language": 3,
                "Organization": 4
            },
            "proofread_summary": null,
        },
        {
            "id": 1235,
            "created": "2019-03-20T14:57:42Z",
            "title": "The answer title",
            "html": "<p>The student's answer text</p>",
            "reviewer_comment": "An instructor's comment",
            "author_comment": "A student's comment",
            "is_submission": false,
            "answer_type": "proofread",
            "signal_check_scores": null,
            "proofread_summary": {
                "Mechanics": 1,
                "Usage": 2,
                "Grammar": 1,
            },
          }
      ]
    }
    

    Path: GET /api/contexts/:urlencoded-context-id/resource-links/:urlencoded-resource-link-id/answers/:urlencoded-student-user-id

    All of a single student's signal checks (also called drafts) within the specified resource link will be returned in chronological order, oldest to newest.

    Each answer will contain the following information:

    Score an Essay

    Payload to score an essay -- note that the student, class, and resource link must already exist:

    # essay.json
    {
      "context_id": "c78d744b",
      "custom_document_identifier": "53a702b3762f",
      "is_submission": false,
      "resource_link_id": "6d74da8f",
      "text": "This is my essay text...",
      "title": "This is my essay title",
      "user_id": "4acb4db8"
    }
    

    Send payload to scoring API:

    curl \
      -H "Authorization: LTI mykey:mysecret" \
      -H "Content-Type: application/json" \
      -d @essay.json
      https://api.tiiscoringengine.com/api/scores
    

    Response includes URL for polling along with identifiers:

    {
      "custom_document_identifier": "53a702b3762f",
      "essay_identifier": "o7seq6o7",
      "url": "https://api.tiiscoringengine.com/api/scores/o7seq6o7"
    }
    

    Polling the document for scores will eventually reveal a response:

    curl \
      -H "Authorization: LTI mykey:mysecret" \
      https://api.tiiscoringengine.com/api/scores/o7seq6o7
    
    {
      "created": "2019-04-01T20:27:08Z",
      "custom_document_identifier": "53a702b3762f",
      "essay_identifier": "o7seq6o7",
      "genre": "Persuasive",
      "status": "completed",
      "task_identifier": "kmf9342z",
      "valid": true,
      "validation_feedback": {},
      "traits": [{
        "score": 2,
        "short_name": "Claim",
        "style_key": "claim-and-focus",
        "trait_name": "Claim and Focus",
        "essay_feedback": {
          "question": "How well do I make a claim and stay focused on proving it throughout my essay?",
          "statement": "State a clear position in the introduction on..."
        }
      }, {
        "score": 2,
        "short_name": "Support",
        "style_key": "support-and-evidence",
        "trait_name": "Support and Evidence",
        "essay_feedback": {
          "question": "How well do I use evidence to persuade the audience of my position?",
          "statement": "Select appropriate examples to support your position..."
        },
      }, ...
      ]
    }
    

    Path: POST /api/scores

    This will queue an essay for scoring, giving you back a URL you can poll for results. When you poll the URL you'll get a 204 No Content if the essay's scores haven't been calculated yet. If they have the response will include the scores for each of the traits in the rubric genre, but not sentence-level feedback. (Sentence-level feedback is still in the system and if you view the essay in Revision Assistant you'll see it in the UI, we just don't have a good way to return the feedback in a usable manner via API.)

    Note that you'll need to have this feature enabled to use it, contact the Revision Assistant team to make that happen. (If it's not enabled you'll get back a 403 Forbidden error.)

    Data we expect in the essay to be scored is:

    We will return a 404 if any of the following is true:

    The returned document will have the following keys:

    Once the essay has been scored the status from the given url will be 200, and the response will contain:

    Fetch a User

    Fetch a student:

    curl \
      -H "Authorization: mykey:mysecret" \
      https://api.tiiscoringengine.com/api/users/6bf6b34b
    
    {
      "user_id": "6bf6b34b",
      "first_name": "Richard",
      "last_name": "Grayson",
      "additional_identifiers": [
        {"identifier_type": "sis", "identifier": "811556dc-8882-4b06-9998-a52223481aa7"}
      ]
    }
    

    Fetch a student with details:

    curl \
      -H "Authorization: mykey:mysecret" \
      https://api.tiiscoringengine.com/api/users/6bf6b34b/details
    
    {
      "user": {
        "user_id": "6bf6b34b",
        "first_name": "Richard",
        "last_name": "Grayson",
        "additional_identifiers": [
          {"identifier_type": "sis", "identifier": "811556dc-8882-4b06-9998-a52223481aa7"}
        ]
      },
      "contexts": [{
        "context_created": "2019-01-29T19:21:38Z",
        "context_id": "9a1a97d1",
        "context_state": "active",
        "context_title": "9 - English - Period 1",
        "membership_created": "2019-01-29T19:21:38Z",
        "membership_ended": "",
        "membership_type": "Learner"
      }]
    }
    

    Path: GET /api/users/:urlencoded-student-guid

    Path: GET /api/users/:urlencoded-student-guid/details

    Retrieves information about an individual user. The basic version just includes information about the user record, the /details version also includes contexts to which the user currently belongs and may be later augmented with things like assignments, login/launch activity, etc.

    Delete a Student

    Path: DELETE /api/users/:urlencoded-student-guid

    NOTE: This operation cannot be undone. So be careful!

    This will remove the student's Personally Identifiable Information (PII) from Revision Assistant and remove them from all classes. This will also free up the GUID (LTI User ID) associated with it so you can re-use if desired.

    By default the student's writing will remain in the system for the purposes of analysis, but it will no longer be associatable with the person who wrote it by way of its metadata.

    You can override this behavior with the query arg remove_essays=true. Processing a request with that will remove the student's PII immediately and create a job to wipe the student's essays from the system. That job is guaranteed to be processed within 24 hours but in practice will be processed within seconds, so don't rely on the fact that it's a background job to undo the action. However, before doing that you must contact Revision Assistant support to enable this feature -- if you don't you'll get back a 403 error telling you your account is not enabled to delete student essays.

    Also, note this will not remove instructor records, and if the specified user record has any active Instructor memberships no action will be taken and you'll receive a 400 error.

    Finally, this will return a 404 Not Found if the given student does not exist within your consumer.

    Describe a prompt's rubric

    Fetch the rubric information for 'historicalanalysis' genre:

    curl -H "Authorization: LTI mykey:mysecret" \
      https://staging.tiiscoringengine.com/api/prompts/genres/historicalanalysis
    

    Response:

    {
      "slug": "historicalanalysis",
      "title": "Historical Analysis",
      "traits": [
        {
          "scores": {
            "1": "The essay makes an overly simplistic or vague claim...",
            "2": "The essay makes a claim based on the topic and/or source(s)...",
            "3": "The essay makes a clear claim based on the topic and/or source(s)...",
            "4": "The essay makes a precise and significant claim based on..."
          },
          "short_name": "Claim",
          "slug": "claim",
          "title": "Claim and Focus"
        },
        ...
      ]
    }
    

    Path: POST /api/prompts/genres/:genre-slug

    Rubrics are best presented as PDFs for ease of reading -- if you need a link to the rubric for a particular prompt let us know. It can be useful to access the data programmatically, and for that you can call this transaction.

    Available genres are:

    Managing Credentials and Groups

    If enabled for your account you can:

    The main reason you'll want to create groups and subgroups is for reporting purposes. Without grouping the assignments teachers and students launch into you'll just have a big pool of usage. You can segment it by teacher but that's it.

    Creating groupings allows you to segment usage much more granularly. For example, say I created the following groups:

    I can now group usage by either of the districts, which will roll up usage from its schools. Or I can group it by any of the schools.

    Creating groupings also allows you to control access to the usage data. You can assign administrative users to a district or a school and they'll only see the relevant data.

    List groups

    List all groups (above Classes); we'll start with your top level group:

    curl \
      -H 'Authorization: LTI ZMLTH-TRDB-SDBK:FTXXNSEHZXZLNHH' \
      https://api.tiiscoringengine.com/api/groups
    
    200 OK
    {
      "groups": [
        {
          "created": "2020-05-15T19:28:50Z",
          "credentials": [{
            "consumer_key": "ZMLTH-TRDB-SDBK",
            "consumer_secret": "FTXXNSEHZXZLNHH",
            "identifier": "jy9pt3wx",
            "lti_version": "1.1"
          }],
          "custom_identifier": null,
          "identifier": "pkc685m9",
          "name": "Top Organization Test",
          "parent_identifier": null,
          "type": "Organization"
        }
      ]
    }
    

    Groups are fairly simple and have few attributes associated with them:

    Each group also has one or more associated credentials. We discuss those more below.

    Provision districts and institutions

    Create a new district under your tenant account:

    # new_district.json
    {
      "name": "District One",
      "custom_identifier": "60fdae71-558a-4ac6-aab4-3770675c0e7f",
      "parent_identifier": null,
      "type": "District"
    }
    
    curl \
      -H "Content-Type: application/json" \
      -H "Authorization: LTI ZMLTH-TRDB-SDBK:FTXXNSEHZXZLNHH" \
      -d @new_district.json
      https://api.tiiscoringengine.com/api/groups
    
    201 Created
    {
      "created": "2020-05-15T19:57:46Z",
      "credentials": [
        {
          "consumer_key": "ZMLTH-TRDB-SDBK-wzc3erbr",
          "consumer_secret": "FTXXNSEHZXZLNHH",
          "identifier": "jy9pt3wx",
          "lti_version": "1.1"
        }
      ],
      "custom_identifier": "60fdae71-558a-4ac6-aab4-3770675c0e7f",
      "identifier": "wzc3erbr",
      "name": "District One",
      "parent_identifier": "pkc685m9",
      "type": "District"
    }
    

    Create a new institution under that district:

    # new_institution.json
    {
      "name": "School One",
      "custom_identifier": "f65ba87a-d8d8-4ed3-be17-060de28696dd",
      "parent_identifier": "wzc3erbr",
      "type": "Institution"
    }
    
    curl \
      -H 'Authorization: LTI ZMLTH-TRDB-SDBK:FTXXNSEHZXZLNHH' \
      -H "Content-Type: application/json" \
      -d @new_institution.json
      https://api.tiiscoringengine.com/api/groups
    
    201 Created
    {
      "created": "2020-05-15T20:05:39Z",
      "credentials": [
        {
          "consumer_key": "ZMLTH-TRDB-SDBK-evcwpk4o",
          "consumer_secret": "FTXXNSEHZXZLNHH",
          "identifier": "jy9pt3wx",
          "lti_version": "1.1"
        }
      ],
      "custom_identifier": "f65ba87a-d8d8-4ed3-be17-060de28696dd",
      "identifier": "evcwpk4o",
      "name": "School One",
      "parent_identifier": "wzc3erbr",
      "type": "Institution"
    }
    

    List districts and institutions, plus your top level group

    curl \
      -H 'Authorization: LTI ZMLTH-TRDB-SDBK:FTXXNSEHZXZLNHH' \
      https://api.tiiscoringengine.com/api/groups
    
    200 OK
    {
      "groups": [
        {
          "created": "2020-05-15T19:57:46Z",
          "credentials": [{
            "consumer_key": "ZMLTH-TRDB-SDBK-wzc3erbr",
            "consumer_secret": "FTXXNSEHZXZLNHH",
            "identifier": "jy9pt3wx",
            "lti_version": "1.1"
          }],
          "custom_identifier": "60fdae71-558a-4ac6-aab4-3770675c0e7f",
          "identifier": "wzc3erbr",
          "name": "District One",
          "parent_identifier": "pkc685m9",
          "type": "District"
        },
        {
          "created": "2020-05-15T20:05:39Z",
          "credentials": [{
            "consumer_key": "ZMLTH-TRDB-SDBK-evcwpk4o",
            "consumer_secret": "FTXXNSEHZXZLNHH",
            "identifier": "jy9pt3wx",
            "lti_version": "1.1"
          }],
          "custom_identifier": "f65ba87a-d8d8-4ed3-be17-060de28696dd",
          "identifier": "evcwpk4o",
          "name": "School One",
          "parent_identifier": "wzc3erbr",
          "type": "Institution"
        },
        {
          "created": "2020-05-15T19:28:50Z",
          "credentials": [{
            "consumer_key": "ZMLTH-TRDB-SDBK",
            "consumer_secret": "FTXXNSEHZXZLNHH",
            "identifier": "jy9pt3wx",
            "lti_version": "1.1"
          }],
          "custom_identifier": null,
          "identifier": "pkc685m9",
          "name": "Top Organization Test",
          "parent_identifier": null,
          "type": "Organization"
        }
      ]
    }
    

    Creating groups within the API is straightforward. You provide the few pieces of information you can (name, custom_identifier, and type) along with where the group belongs in the hierarchy (using parent_identifier).

    There are a few constraints to keep in mind when creating groups:

    Note that we return a flat list of groups rather than nesting children under parents. It's possible we could modify the API to optionally return them nested, let us know if that's useful for you.

    Also, note that the identifier for a group may also be used in:

    List credentials

    List available credentials:

    curl \
      -H 'Authorization: LTI ZMLTH-TRDB-SDBK:FTXXNSEHZXZLNHH' \
      https://api.tiiscoringengine.com/api/credentials
    
    200 OK
    {
      "credentials": [
        {
          "active": true,
          "api_last_used": "2020-06-01T16:39:11Z",
          "consumer_key": "ZMLTH-TRDB-SDBK",
          "consumer_secret": "FTXXNSEHZXZLNHH",
          "created": "2020-05-15T19:28:51Z",
          "expires": "",
          "identifier": "jy9pt3wx",
          "launch_last_used": "2020-05-28T11:57:30Z",
          "lti_version": "1.1"
        }
      ]
    }
    

    You can associate one or more credentials with your account. By default you always have one -- it's the one you use for this API! But you can create more than one credential that's active at a time, and you can also set an expiration for a credential to ensure you can deploy the necessary changes on your side without a bunch of coordination with people at Revision Assistant.

    The transaction above shows the credentials associated with your account. You'll also notice when you list groups that each group has a similar but slightly different LTI credential. This slightly different credential is used in LTI launches and is how we distinguish one school from another.

    The difference is the trailing identifier, and it's one of two ways we know where to place a class when an LTI launch happens for the first time. (The other is using the LTI launch parameter custom_parent_group_identifier, see docs above.)

    Add a credential

    Create a new credential

    # new_credential.json
    {
      "consumer_key": "d9e086e1-f135-485d-9e6a-caabd8eec4d6",
      "consumer_secret": "7f797a72-7cdf-4ee4-8d9b-c02ce31ed340",
      "lti_version": "1.1"
    }
    
    curl \
      -H "Content-Type: application/json" \
      -H 'Authorization: LTI ZMLTH-TRDB-SDBK:FTXXNSEHZXZLNHH' \
      -d @new_credential.json \
      https://api.tiiscoringengine.com/api/credentials
    
    {
      "active": true,
      "api_last_used": "",
      "consumer_key": "d9e086e1-f135-485d-9e6a-caabd8eec4d6",
      "consumer_secret": null,
      "created": "2020-06-01T16:42:47Z",
      "expires": "",
      "identifier": "3yv3t160",
      "launch_last_used": "",
      "lti_version": "1.1"
    }
    

    List credentials after create, new credential is there:

    curl \
      -H 'Authorization: LTI ZMLTH-TRDB-SDBK:FTXXNSEHZXZLNHH' \
      https://api.tiiscoringengine.com/api/credentials
    
    {
      "credentials": [
        {
          "active": true,
          "api_last_used": "",
          "consumer_key": "d9e086e1-f135-485d-9e6a-caabd8eec4d6",
          "consumer_secret": null,
          "created": "2020-06-01T16:42:47Z",
          "expires": "",
          "identifier": "3yv3t160",
          "launch_last_used": "",
          "lti_version": "1.1"
        },
        {
          "active": true,
          "api_last_used": "2020-06-01T16:56:41Z",
          "consumer_key": "ZMLTH-TRDB-SDBK",
          "consumer_secret": "FTXXNSEHZXZLNHH",
          "created": "2020-05-15T19:28:51Z",
          "expires": "",
          "identifier": "jy9pt3wx",
          "launch_last_used": "2020-05-28T11:57:30Z",
          "lti_version": "1.1"
        }
      ]
    }
    

    You can add new credentials at any time, and multiple credentials can be active at once -- that is, you can use any active credential to either authenticate to this API or to perform an LTI launch. You may find this useful to change credentials on your side -- having multiple credentials be active means you can change them without a big flip of the switch, or waiting until 2 AM when few people are using your system.

    So rolling your credential may look like this:

    Expire or deactivate a credential

    Set an expiration for a credential:

    curl \
      -H 'Authorization: LTI ZMLTH-TRDB-SDBK:FTXXNSEHZXZLNHH' \
      -H 'Content-Type: application/json' \
      -d '{"expires": "2020-06-15T00:00:00Z"}' \
      https://api.tiiscoringengine.com/api/credentials/3yv3t160
    
    {
      "active": true,
      "api_last_used": "",
      "consumer_key": "d9e086e1-f135-485d-9e6a-caabd8eec4d6",
      "consumer_secret": null,
      "created": "2020-06-01T16:42:47Z",
      "expires": "2020-06-15T00:00:00Z",
      "identifier": "3yv3t160",
      "launch_last_used": "",
      "lti_version": "1.1"
    }
    

    Immediately deactivate a credential:

    curl \
      -H 'Authorization: LTI ZMLTH-TRDB-SDBK:FTXXNSEHZXZLNHH' \
      -H 'Content-Type: application/json' \
      -d '{"active": false}' \
      https://api.tiiscoringengine.com/api/credentials/3yv3t160
    
    {
      "active": false,
      "api_last_used": "",
      "consumer_key": "d9e086e1-f135-485d-9e6a-caabd8eec4d6",
      "consumer_secret": null,
      "created": "2020-06-01T16:42:47Z",
      "expires": "2020-06-15T00:00:00Z",
      "identifier": "3yv3t160",
      "launch_last_used": "",
      "lti_version": "1.1"
    }
    

    The only two pieces of information you can modify in a credential are:

    Generating Data

    There are a couple ways to generate data. Note that these transactions should not be run in production, and are meant so you can exercise different aspects of the API without having to launch the front-end and write essays.

    Data returned should generally have external identifiers where appropriate -- assignments should have lti_resource_link_id, classes should have lti_context_id, users should have lti_user_id -- so you can correlate them to data in your system.

    Generate classes

    Generate a class in my district:

    $ curl -X POST \
        -H 'Authorization: LTI PGKWP-PNPM-PAMB:SXBLBHEGXYGDSRR' \
        https://api.tiiscoringengine.com/api/generators/classes
    
    {
      "results": [{
        "assignments": [{
          "assignment": {
            "assignment_type": "signal check",
            "class_id": 2253,
            "created": "2018-04-18T03:41:47Z",
            "id": 3894,
            "instructor_id": 11055,
            "lti_resource_link_id": "WFDY-37-PEEYYA",
            "name": "Intro to Engineer, electronics",
            "prompt_id": 442,
            "state": "open"
          },
          "prompt": {
            "created": "2017-07-28T13:13:31Z",
            "genre": "Analysis",
            "genre_slug": "analysis",
            "id": 442,
            "max_grade_level": "12",
            "min_grade_level": "9",
            "rubric_file": "https://.../foo-6d65e532f051_rubric.pdf",
            "slug": null,
            "title": "Big Data",
            "traits": [
              "Claim and Focus",
              "Analysis and Evidence",
              "Organization",
              "Language and Style"
            ]
          }
        }],
        "classroom": {
          "created": "2018-04-18T03:41:47Z",
          "id": 2253,
          "instructor_ids": null,
          "lti_context_id": "LNXXLX-92-RHNG",
          "name": "Delgado-Thompson",
          "state": "active",
          "student_ids": null
        },
        "instructor": {
          "created": "2018-04-18T03:41:46Z",
          "display_name": "Michael Ingram",
          "email": "michael.ingram-8468@example.com",
          "first_name": "Michael",
          "id": 11055,
          "last_name": "Ingram",
          "lti_user_id": "11055",
          "username": null
        },
        "students": [{
          "created": "2018-04-18T03:41:46Z",
          "display_name": "Cindy Clay",
          "email": "cindy.clay-3328@example.com",
          "first_name": "Cindy",
          "id": 11056,
          "last_name": "Clay",
          "lti_user_id": "11056",
          "username": null
        },
          ...
        ]
        }
      ]
    }
    

    Generate 10 classes in my district:

    $ curl \
        -d '{"class_count": 10}' \
        -H 'Content-Type: application/json' \
        -H 'Authorization: LTI PGKWP-PNPM-PAMB:SXBLBHEGXYGDSRR' \
        https://api.tiiscoringengine.com/api/generators/classes
    ...
    

    Path: POST /api/generators/classes

    There's an API call you can make to generate realistic looking data for an entire class. A call to generate classes will create for each one:

    Note that, depending on how many classes you create, the essays won't have scores immediately, but they should quite soon.

    Once these data are created you can manipulate them at will. For example, once you get a context ID you can get its details with the API call above. Or remove the assignment, or any of the users.

    Generate assignments

    Generate an assignment in one of my contexts:

    $ curl \
        -d '{"lti_context_id": "LNXXLX-92-RHNG"}' \
        -H 'Authorization: LTI PGKWP-PNPM-PAMB:SXBLBHEGXYGDSRR' \
        https://api.tiiscoringengine.com/api/generators/assignments
    
    {
      "results": [{
        "answer_summaries": [
          {"answer_count": 2, "identity_id": 11056, "lti_user_id": "11056"}
        ],
        "assignment": {
           "assignment_type": "signal check",
           "class_id": 2253,
           "created": "2018-04-18T03:41:47Z",
           "id": 3894,
           "instructor_id": 11055,
           "lti_resource_link_id": "WFDY-37-PEEYYA",
           "name": "Intro to Engineer, electronics",
           "prompt_id": 442,
           "state": "open"
        },
        "prompt": {
          "created": "2017-07-28T13:13:31Z",
          "genre": "Analysis",
          "genre_slug": "analysis",
          "id": 442,
          "max_grade_level": "12",
          "min_grade_level": "9",
          "rubric_file": "https://.../foo-6d65e532f051_rubric.pdf",
          "slug": null,
          "title": "Big Data",
          "traits": [
            "Claim and Focus",
            "Analysis and Evidence",
            "Organization",
            "Language and Style"
          ]
        }
      }]
    }
    

    Generate 10 assignments in my class:

    $ curl \
        -d '{"assignment_count": 10, "lti_context_id": "LNXXLX-92-RHNG"}' \
        -H 'Content-Type: application/json' \
        -H 'Authorization: LTI PGKWP-PNPM-PAMB:SXBLBHEGXYGDSRR' \
        https://api.tiiscoringengine.com/api/generators/assignments
    ...
    

    Path: POST /api/generators/assignments

    There's an API call you can make to add assignments to existing classes. A call to generate assignments will create for each one:

    Note that, depending on how many assignments you create, the essays won't have scores immediately, but they should shortly after the transaction is complete, depending on the number of assignments created and the number of students in the class.

    Once these data are created you can manipulate them at will. For example, once you get a context ID you can get its details with the API call above. Or remove the assignment, or any of the users.

    You'll get a 400 Bad Request back if the context ID you provided doesn't exist in your consumer.

    Reports

    You can query into the activity of users in Revision Assistant. The reporting API allows you to:

    We currently support one event for querying, student drafts (api_drafts).

    Note that this feature must be turned on for your account; if you try to use it before it's enabled you'll get a 403 response back with a message about the feature being disabled.

    Common reporting concerns

    Data formats

    Requests

    {
     "report_type": "some type",
     "external_id": "optional unique identifier that we'll pass back",
     "criteria": {
       "field": "value",
       "field_two": "value"
     }
    }
    

    Report requests are all structured the same way and include:

    Look in each report below for its report_type and supported criteria.

    Initial responses

    Here's a sample report request and response:

    # report_1.json
    {
      "report_type": "api_drafts",
      "external_id": "1265F70F",
      "criteria": {
        "lti_context_id": "024048B2C4ED",
        "draft_create_after": "2018-03-01T00:00:00Z"
      }
    }
    
    curl \
      -H 'Content-Type: application/json' \
      -H 'Authorization: LTI FOO:BAR' \
      -d @report_1.json \
      https://staging.tiiscoringengine.com/api/reports
    
    {
      "report_type": "api_drafts",
      "status": "queued",
      "external_id": "1265F70F",
      "criteria": {
        "lti_context_id": "024048B2C4ED",
        "draft_create_after": "2018-03-01T00:00:00Z"
      },
      "report_key": "2b8ef",
      "url": "https://staging.tiiscoringengine.com/api/reports/2b8ef",
      "error": null,
      "results": null,
      "runtime": {
        "started": null,
        "completed": null,
        "elapsed_secs": null,
        "data_refreshed": null,
        "results_time_to_live_secs": null
      }
    }
    

    And here's a sample request and response for a query where the specified data refresh time is after the actual -- note the status of "skip":

    # report_2.json
    {
      "report_type": "api_drafts",
      "criteria": {
        "lti_context_id": "024048B2C4ED",
        "draft_create_after": "2018-03-01T00:00:00Z",
        "data_refreshed": "2018-08-10T14:50:00Z"
      }
    }
    
    curl \
      -H 'Content-Type: application/json' \
      -H 'Authorization: LTI FOO:BAR' \
      -d @report_2.json \
      https://staging.tiiscoringengine.com/api/reports
    
    {
      "report_type": "api_drafts",
      "status": "skip",
      "external_id": "",
      "criteria": {
        "lti_context_id": "024048B2C4ED",
        "draft_create_after": "2018-03-01T00:00:00Z",
        "data_refreshed": "2018-08-10T14:50:00Z"
      },
      "report_key": "fab39",
      "url": "https://staging.tiiscoringengine.com/api/reports/fab39",
      "error": null,
      "results": null,
      "runtime": {
        "started": "2018-08-10T15:08:02Z",
        "completed": "2018-08-10T15:08:02Z",
        "elapsed_secs": 0.0,
        "data_refreshed": "2018-08-10T14:40:02Z",
        "results_time_to_live_secs": null
      }
    }
    

    Report responses are all structured the same way. The first response you'll get after submitting a report will likely contain some blanks because it hasn't yet been processed. But it will include:

    Response with results

    Extending earlier example to get results:

    curl \
      -H 'Authorization: LTI FOO:BAR' \
      https://staging.tiiscoringengine.com/api/reports/2b8ef
    
    {
      "report_type": "api_drafts",
      "status": "complete",
      "external_id": "1265F70F",
      "criteria": {
        "lti_context_id": "024048B2C4ED",
        "draft_create_after": "2018-03-01T00:00:00Z"
      },
      "report_key": "2b8ef",
      "url": "https://api.tiiscoringengine.com/api/reports/2b8ef"
      "results": {
        "assignments": [
            {...},
        ],
        "classes": [
            {...},
        ],
        "drafts": [
            {...},
        ],
        ...
      },
      "runtime": {
        "started": "2018-04-05T12:34:56Z",
        "completed": "2018-04-05T12:34:58Z",
        "elapsed_secs": 1.892,
        "data_refreshed": "2018-04-05T12:30:00Z",
        "results_time_to_live_secs": 3600
      },
      "error": null,
    }
    

    Optionally, delete the report results after you're done procesing:

    curl -X DELETE \
      -H 'Authorization: LTI FOO:BAR' \
      https://staging.tiiscoringengine.com/api/reports/2b8ef
    
    204 No Content
    

    Polling the given URL you'll eventually get data filled in to the results and runtime keys.

    The only data that will be substantially different from report to report is that in results, which will depend on the type of report being run.

    The report is cached for some period of time, typically an hour. If you request the report after that cache time has expired you'll get a 404 response. When that happens you should re-run the report and get updated results back.

    The data in the runtime key is the same no matter what type of report you're running:

    This is also the key and value you'd use when including the refresh time in your query.

    Note that you may also issue a DELETE against the polling URL to remove the reporting results from internal caches. This helps us out by keeping things tidy, especially if you've arranged with the Revision Assistant engineering team for a longer cache time.

    Report: Student drafts

    Report type: api_drafts

    This report includes information about every draft students write in Revision Assistant, though it is currently limited to signal check assignments so does not include drafts for spot check or for unscored expansion pack assignments.

    Criteria

    JSON Results

    Here are the top-level keys you'll find in results; we'll give details of each of them below:

    Assignments:

    Each record in the assignments array will be one referenced by a draft in the resultset by drafts.assignment_id.

    Classes:

    Each record in the classes array will be one referenced by a draft in the resultset by drafts.class_id.

    Drafts:

    Each record in the drafts array represents a draft written by a student.

    Instructors:

    Each record in the instructors array will be one referenced by a draft in the resultset by either drafts.identity_assignment_creator_id or drafts.identity_class_creator_id.

    Prompts:

    Each record in the prompts array will be one referenced by a draft in the resultset by drafts.prompt_id as well as an assignment by assignments.prompt_id. It's possible the same functional prompt will be represented multiple times based on internal versioning information.

    Students:

    Each record in the students array will be one referenced by a draft in the resultset by drafts.student_id

    CSV Results

    If you request a CSV the return data will include the following base values:

    Additionally you'll have columns corresponding to all genre traits represented by all prompt genres. These will hold the students' scores for each draft. For example, if any of the drafts is associated with the Historical Analysis genre you'd have additional columns as follows, each representing one of the traits in the genre's rubric:

    Turnitin Scoring Engine (TSE)

    Turnitin Scoring Engine is a separate small set of transactions that allow you to score essays without having to create a class, assignment, or user beforehand. However:

    Every call requires a TSE-specific API token. You'll provide it in the Authorization header like Authorization: Token my-tse-token. You'll see its use in the examples at right.

    List Assignments

    List my assignments

    $ curl -H 'Authorization: Token ea598291-7666' \
        https://api.tiiscoringengine.com/tse/prediction/assignments
    
    {
      "assignments": [
        {"assignment_id": 81955, "name": "Expect the Unexpected"},
        {"assignment_id": 84527, "name": "Industrialization: Luxembourg vs the World"}
      ]
    }
    

    Path: GET /tse/prediction/assignments

    Lists all assignments available for TSE essay scoring. Note: you should not rely on assignment_id being an integer, it may become a string in the future.

    Score Essay

    Score an essay

    # my-essay.json
    {
      "identifier": "1bb17bac-b2c7-49dd-a25e-5197a70f0eba",
      "text": "This is my essay. There are many like it, but this one is mine...",
      "assignment_id: 81955
    }
    
    $ curl \
        -d @my-essay.json \
        -H 'Authorization: Token ea598291-7666' \
        -H 'Content-Type: application/json' \
        https://api.tiiscoringengine.com/tse/prediction
    
    {
      "answer_id": 5899581,
      "created": "2021-08-27T18:05:24Z",
      "predicted": false
    }
    

    Path: POST /tse/prediction

    Submit an essay for scoring. If successful you'll get back a 201 Created status and a document that includes an answer_id field; you'll need this to fetch the scores once it's predicted (next transaction).

    Note: you should not count on answer_id being an integer, it may change to a string in the future.

    Some common error conditions are as follows, see Errors for the structure of the returned document:

    Fetch Scores

    ```shell
    $ curl \
        -H 'Authorization: Token ea598291-7666' \
        https://api.tiiscoringengine.com/tse/prediction/5899581/score
    
    {
      "answer_id": 5899581,
      "assignment_id": 89155,
      "created": "2021-08-27T18:05:24Z",
      "predicted": true,
      "prediction_results": [
        {
          "dimension_id": "Informative_Clarity",
          "score": 1,
          "valid": false,
          "validation_results": {
            "student_title": "This draft was too short.",
            "validator_name": "too_short"...
          }
        },
        {
          "dimension_id": "Informative_Development",
          "score": 1,
          "valid": false,
          "validation_results": {
            "student_title": "This draft was too short.",
            "validator_name": "too_short"...
          }
        },
        {
          "dimension_id": "Informative_Organization",
          "score": 1,
          "valid": false,
          "validation_results": {
            "student_title": "This draft was too short.",
            "validator_name": "too_short"...
          }
        },
        {
          "dimension_id": "Informative_Language",
          "score": 1,
          "valid": false,
          "validation_results": {
            "student_title": "This draft was too short.",
            "validator_name": "too_short"...
          }
        }
      ]
    }
    

    Path: GET /tse/prediction/{answer_id}/score`

    Once you've submitted an essay for scoring you'll poll a URL until you get results. Until the essay is predicted you'll get back a 204 No Content status.

    Note that the dimension_id values vary based on the rubric of the prompt you're using.

    Also, if you specify an answer_id that doesn't exist, or that doesn't belong to your account, you'll get back a 404 Not Found status.

    Errors

    The API uses conventional HTTP response status codes to indicate the status or failure of an API call. In general, 2xx codes indicate success, 4xx codes indicate an error in the user-supplied information, and 5xx codes indicate an error with the server.

    More specifically you'll see:

    HTTP Status Short Name Meaning
    200 OK Everything was processed and the changes are reflected in the response.
    202 Accepted We got your request and have queued your job for processing. The response should include some sort of identifier for the job being processed so you can ask for the latest status of the job.
    204 No Content Everything processed but there's no response.
    400 Bad Request Something was wrong with your request data, or it has a conflict with the existing state in the system.
    401 Unauthorized You're either missing an auth token or you have a bad one. See authentication for details
    403 Forbidden You have a valid authentication token but your account cannot perform this action.
    429 Too Many Requests You've exceeded rate limits; the response will provide details.
    500 Internal Server Error Something has gone wrong on the RA/TSE API end. Your response should contain an error code that we can use to lookup the issue and its context.
    503 Service Unavailable System is undergoing maintenance or having problems with availability. See status page for details. (Also see below for standard maintenance response.)

    Bad request error documents

    {
      "errors": {
        "global": ["I am an error"]
        "field": {
          "field-name-one": ["I am error one", "I am error two"],
          "field-name-two": ["I am yet another error"],
          ...
        }
      }
    }
    

    Requests resulting in a 400 or 429 error will have an error document in a shape that accommodates multiple global or field-level errors.

    You should not receive a per-field entry in fields unless there's at least one error.

    Standard maintenance response

    {
      "errors": {
        "global": ["Revision Assistant is currently undergoing system maintenance. We'll be back up soon!"],
        "field": {}
      }
    }
    

    When Revision Assistant is undergoing planned maintenance you'll get the above JSON response along with a 503 status. Check the RA status page for details on the time window for the maintenance.