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:
- Production: https://api.tiiscoringengine.com/api/docs
- Staging: https://staging.tiiscoringengine.com/api/docs
(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
andCONSUMER-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:
Authorization: API b14293d243e144c358f90f4b3ecdb80ed0d5f819
Authorization: LTI WAFFLE-1885-CONES:SOMESECRET
Token types are:
API
: general API tokenLTI
: your organization's LTI consumer key and secret, colon-separated
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:
- .NET
- Java
- JavaScript
- PHP
- Python - We use this, and you can see it in action in our simple LTI consumer
- Ruby
Terminology
- Provider -- The service that exposes interfaces to one or more tools -- this is Revision Assistant.
- Consumer -- The application that is embedding tools within itself -- this might be an LMS like Canvas or Blackboard or Schoology, or it might be a unique system that needs to integrate with writing tools and feedback within Revision Assistant. Usually this is something you build or control.
- User -- An individual who uses the consumer. The consumer must define a unique identifier for each user which is then passed to the provider.
- Context -- A set of related users that holds one or more resource links. Contexts are often groups of people such as classes, study groups, etc. The consumer must define a unique identifier for each context that it passes to the provider.
- Resource link -- A reference to a particular set of work in a tool. In Revision Assistant each assignment will be a resource link. The consumer must define and pass the provider an identifier for each resource link. The resource link's identifier must be unique within its context.
Data Flow
An LTI launch into Revision Assistant from your tool will likely look like the following.
- A user signs into your tool.
- The user navigates through your tool until they reach an assignment.
- 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
)
- Context identifier (
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:
- Instructor: You can use any of these aliases to specify an instructor:
- Instructor
- urn:lti:instrole:ims/lis/Instructor
- urn:lti:role:ims/lis/Instructor
- Student: You can use any of these aliases to specify a student:
- Student
- Learner
- urn:lti:instrole:ims/lis/Student
- urn:lti:instrole:ims/lis/Learner
- urn:lti:role:ims/lis/Learner
- urn:lti:role:ims/lis/Learner/Learner
Launch Parameters: Required
OAuth information:
oauth_version
("1.0")oauth_nonce
oauth_signature_method
("HMAC-SHA1")oauth_timestamp
(epoch seconds, for example JavaScript'sDate.now() / 1000
)oauth_signature
oauth_consumer_key
LTI protocol information:
lti_message_type
("basic-lti-launch-request")lti_version
("LTI-1p0")
Contextual launch information:
resource_link_id
: unique identifier for resource links; this will be the external reference for resource links/assignments in reporting aslti_resource_link_id
roles
("Instructor", "Learner", see above)context_id
: unique identifier for context; this will be the external reference for contexts/classes in reporting aslti_context_id
user_id
: unique identifier for user; this will be the external reference for users in reporting asstudent_lti_user_id
orinstructor_lti_user_id
Additionally, the request must contain one or both of:
lis_person_contact_email_primary
lis_person_name_family
ANDlis_person_name_given
Launch Parameters: Optional
context_title
(e.g., "Intro to Philosophy") this becomes the name of the Classlaunch_presentation_css_url
: The stylesheet at this address will be applied after Revision Assistant's stylesheets.launch_presentation_return_url
: If you provide this we'll give the user a "Home" icon in the top navigation. Clicking that icon will take the user to the URL you provide.resource_link_title
(e.g., "Quiz One") this becomes the title of the Assignmenttool_consumer_info_product_family_code
: "exampleconsumer"tool_consumer_info_version
: "0.1"tool_consumer_instance_description
: "Example Consumer"tool_consumer_instance_guid
: "launch.example.com"
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:
lis_outcome_service_url
: The URL we'll call back to with every Signal Checklis_result_sourcedid
: An identifier you can use to match the Revision Assistant call with a student and assignment.
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:
0.0
for when the student clicks "Signal Check"1.0
for when the student clicks "Turn In"
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:
- That the only type of message sent from Revision Assistant is
replaceResult
. - 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:
- 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.)
- 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:
- A context and its backing class,
- synchronize the given set of instructors with the context,
- and synchronize the given roster of students with the context
You can use this to:
- Create a new class
- Create a new class with initial students
- Add new students to an existing class
Data to provision a class are:
context_id
(required): The same identifier you'd use to perform an LTI launch into an assignment for this class.context_title
(required): Name of the class; must be between 3 and 100 characters.parent_group_identifier
(optional): Parent under which to provision this class. If not given we'll place it directly under the group used to authenticate to the API, which is generally your tenant account. Get the identifier from the group management API.
Notes:
- Currently this will not remove students from the context, which means you do not need to send the entire roster with every request. But if you do that's fine.
- We will currently not update student names when synchronizing the roster. However, every time a student launches into an assignment we'll ensure their name matches what your system produces.
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:
- This will return a
404 Not Found
if the given context does not exist in your consumer. - The objects in
instructors
andstudents
will be sorted by last name then first name. - The
state
will be one of:open
(default),archived
, ordeleted
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:
- autosaves, which means they've typed anything at all into that assignment's drafting space,
- prewriting, which means they've typed anything at all into that assignment's prewriting tool,
- drafts, which means they've sent at least one draft to Revision Assistant for scoring and feedback,
- submissions, which means they've clicked the 'Turn In' button for at least one draft to Revision Assistant
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:
- One or more instructors,
- A context,
- A resource link,
- An assignment with a specific prompt,
- and synchronize the roster of students with the context
This request data is very close to creating a new context. The only additional fields are:
resource_link_id
-- a GUID identifying this resource linkresource_link_config
-- a set of data configuring details of the resource link and assignmentinstructions
(optional) -- text that students will see when they write drafts as instructions from the teacher; may also have basic HTML formatting (b
,i
, andu
tags, others will be stripped)prompt_slug
(required) -- the unique identifier for the prompt you want to use for the assignment we'll create. Contact the RA team for a list of valid prompt slugs.title
(required) -- the title students will see in RA for this assignmentcapabilities
(optional) -- one or more features you can disable on the created assignment; more below in "Capabilities".
resource_link_state
(optional) -- the desired state of the resource link at creation; by default it will beopen
but you may also set it toarchived
,closed
, ordeleted
. (And really, onlyclosed
makes sense of that list.) This is to support use cases where you create assignments in bulk at the start of a time period but only want them to beopen
when you trigger it.
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:
- The context, resource link, assignment, and all users will be created if they don't already exist. If they do exist they will not be updated with new data.
- The following fields are the same GUIDs you'll use when you assemble the LTI
launch:
context_id
(same name in launch,lti_context_id
in reporting)resource_link_id
(same name in launch,lti_resource_link_id
in reporting)students.user_id
(same name in launch,student_lti_user_id
in reporting)instructors.user_id
(same name in launch,instructor_lti_user_id
in reporting)
- If you create a resource link without any assignment information
(particularly
resource_link_config.prompt_slug
) then theresource_link_state
field will be "no assignment". This is not a real state and the only time you'll see it is if a resource link exists without an associated assignment. - If you provide multiple resource links in the transaction you'll get multiple resource links back; if you provide one, you'll get one.
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:
draft_history
: Students will see their history of drafts.holistic_feedback
: Students will see the whole-essay feedback along with their scores.inline_feedback
: Students will view and interact with sentence-level feedback when they perform a signal check; may be combined with certain Expansion Pack assignments, check with the product team before doing so.prewriting
: Students will have access to the prewriting feature.proofread
: Students will be able to go into proofread mode to check grammar.teacher_scores
: (Special!) Enables scores to be seen by the teacher and made available for reporting APIs even if a signal check assignment has bothholistic_feedback
andinline_feedback
disabled. It's also the only capability we currently allow to be enabled at assignment creation time:
The capabilities that exist by default for different assignment types are:
- Expansion Pack:
draft_history
holistic_feedback
prewriting
proofread
- Signal Check:
draft_history
holistic_feedback
inline_feedback
prewriting
proofread
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:
- one assignment for one student, or
- one assignment for all students,
- one student for (by default) all open assignments -- that is, an assignment
that does not have a state of 'archived', 'closed', or 'deleted'. You can
specify the states with the query argument
resource_link_state
, multiple is ok. The available states are:open
(default)closed
archived
deleted
- plus a set of states only used for spot check assignments
(
tst_not_started
,tst_in_progress
,tst_collecting
, andtst_collected
)
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:
context_id
: The LTI context ID given to RA at provisioning/launch time,resource_link_id
: The LTI resource link ID given to RA at provisioning/launch time,user_id
: The LTI user ID given to RA at provisioning/launch time,autosave
:true
orfalse
depending on if the student has done any writing in the writing space, even if they haven't done a signal check,prewriting
:true
orfalse
depending on if the student has done any input to the prewriting space,signal_check
: number of signal checks completed, not including submissions (next item),submission
: number of "Turned In" signal checks completed,latest_draft_time
: time the latest signal check was completed,latest_submission_time
: time the latest "Turned In" signal check was completedlatest_scores
: a map of trait-to-score for the most recent scored draft; if empty it means this student has not written any drafts for signal check, or that the most recent draft written by the student is invalid and cannot be scored.
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:
state
: Valid values are:open
: Default, writeable state for an assignment. Students can write drafts against it.closed
: Read-only view of an assignment -- when an assignment is closed students cannot submit any more drafts against it, but they can read and interact with previous ones.archived
: Functionally similar toclosed
but an indication that this was closed due to the end of the schoool year.deleted
: State indicating assignment should no longer be launched by student or teacher.
instructions
: Arbitrary text students will see when they're looking at an assignment. The text can have basic HTML (tags: b, i, u, p, br, ul, ol, li), we'll strip out anything else.title
: Arbitrary text students will see when looking at an assignment; in Revision Assistant it shows up in the upper-left of the student's view. This also corresponds to theresource_link.config.title
attribute you use when creating a resource link. Any HTML tags will be stripped. And while the text will be trimmed to 500 characters if longer it's strongly recommended to be 100 characters or shorter because the Revision Assistant front-end will trim anything longer.capabilities
: One or more features to enable or disable for this assignment, see Capabilities for more. Note that you cannot enable a capability that doesn't exist in an assignment type -- for example, you cannot addinline_feedback
to a Spot Check assignment.
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:
- "user_id": The LTI user identifier of the student we're moving
- "source_context_id": The LTI context identifier of the class we're moving
the student from (the source).
- Note that you can specify "ANY" for this and we'll do the transfer from ALL the student's contexts and assignments to the target context. The policy you specify will apply to all of them, so if you want to do something different the best way forward is to fetch all the user's contexts (see Fetch a User) and transfer each one individually.
- "target_context_id": The LTI context identifier of the class we're moving the student to (the target); by default the student may already belong to the class and the process will work, see "can_belong_to_target" below
- "policy": Guidance on what to do if all the assignments don't map from one
class to another:
- "best effort" (default if unspecified): move student work from the source context to a corresponding assignment in the target context if one exists, otherwise leave the work where it is and do not end the student membership to the source context
- "best effort and end" similar to the above, but we will always end the student membership to the source context, even if all assignments were not matched and their work not moved. This is useful when you want to ensure a student can only belong to one class at a time.
- "all or none": only move student work if every assignment in the source context matches one in the target context; if that's not true do nothing.
- "can_belong_to_target": (optional, defaults to
true
) set tofalse
if you want to ensure that the student cannot already belong to the target class
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:
- "message": "OK" if things worked (even partially), an error message if not
- "status": ovreall status of this operation
- "fail" if an error occurred
- "none", "some", "all" for the amount of work moved over
- "halt" if the policy was "all or none" and we failed to match an assignment from the source context to one in the target.
- "source_membership_status":
- "no action" if the membership wasn't modified
- "ended" if the membership was ended
- "target_membership_status":
- "no action" if we did nothing, or if the membership already existed
- "created" if the membership was created
- "resource_link_status": an array of maps each with:
- "source_resource_link_id": unique identifier for the resource link in the source context
- "target_resource_link_id": unique identifier for the resource link in the target context, may be null if not matched
- "prompt_slug": internal identifier for prompt -- this is what we use to match assignments from the source context to the target
- "status":
- "moved" if we moved the student work
- "not matched" if the prompt slug wasn't matched
- "matched" if the prompt slug was matched but we didn't move it due to the "all or none" policy
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:
id
: The internal ID of the answer in the Revision Assistant system.created
: The time the answer was created in UTC and in the ISO 8601 format.title
: The title of the answer.html
: The entire answer contents, formatted as an HTML snippet.author_comments
: Any comments entered by the student at the time of turning in the answer.reviewer_comments
: Any comments entered by the reviewer (instructor) after reading the student's answer.is_submission
:true
if the student used theTurn In
button to submit their answer, otherwisefalse
.answer_type
: One of the following:no_feedback
: Answers that do not have any automated feedback (e.g. Expansion Pack assignments created before 2019)signal_check
: For signal check assignments, if the answer is valid its scores will be included in thesignal_check_scores
field.proofread
: A draft that was submitted in proofread mode. A summary of the proofread issues found in that draft will appear in theproofread_summary
field.
signal_check_scores
: A dictionary of trait name to score.proofread_summary
: A dictionary of Proofread category type (e.g. Mechanics, Usage, etc.) to the count of issues in that category.
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:
context_id
: LTI context ID of classcustom_document_identifier
: Arbitrary string from you to associate with the result; this may be useful to correlate data in Revision Assistant to your own,is_submission
(defaults tofalse
):true
if this is a submission, which is what happens when the students click the 'Turn In' button in Revision Assistant orfalse
if notresource_link_id
: LTI resource link ID of assignmenttext
: Text of essay, basic HTML oktitle
(defaults to blank): Title of essayuser_id
: LTI user ID of essay author
We will return a 404 if any of the following is true:
- The
context_id
,resource_link_id
, oruser_id
are not found in the system for your account. - The given
resource_link_id
is not found associated with the givencontext_id
.
The returned document will have the following keys:
custom_document_identifier
: The identifier you associated with this essay.essay_identifier
: An internal identifier for this essay.url
: The URL to poll for scores; it contains the essay identifier which is tied to your account. Fetching an identifier for an answer not in your account, even if it's a valid identifier, will generate a 404.
Once the essay has been scored the status from the given url
will be 200, and
the response will contain:
custom_document_identifier
: The identifier you provided in the request.essay_identifier
: The hashed internal identifier for this essay (only useful for debugging)genre
: The rubric genre associated with this assignment.status
: Should only becompleted
.task_identifier
: The hashed internal identifier for the task generating these scores (only useful for debugging)valid
:true
if this essay is valid,false
if notvalidation_feedback
: If this essay is not valid this will contain more information about why (typically it's either off-topic or too short)traits
: An array of items each with:score
: An integer from 1 to 4. (May possibly be a different range if the rubric you're scoring against supports it, but if it does you'll already know -- this is very rare.)short_name
: Short name of trait (may be useful for reporting if you have narrow columns)style_key
: A name suitable for using in CSS classestrait_name
: Full display name of traitessay_feedback
: Trait-specific feedback about the whole essayquestion
: Question the user seesstatement
: Statement about the essay's quality for this trait.
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:
historicalanalysis
: Historical Analysis
Managing Credentials and Groups
If enabled for your account you can:
- provision districts and institutions by API
- modify basic information about districts and institutions
- add, disable, or expire LTI authentication credentials
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:
- District Bear
- School Grizzly
- School Koala
- School Panda
- District Bird
- School Eagle
- School Hawk
- School Raven
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:
name
: A human-readable name that you assign. The only constraint is that it must be unique among its siblings -- so you can't have two groups named "Washington High School" in a District.custom_identifier
: An identifier that you assign. It may be used for querying, and should also accompany the group anywhere you see it (reporting, etc).identifier
: A unique identifier for this group among all groups within Revision Assistant, generated by Revision Assistant.parent_identifier
: A unique identifier for this group's parent among groups within Revision Assistant, generated by Revision Assistant. This value will benull
for the top-most group in your hierarchy.type
: One of "Organization", "District", or "Institution".
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:
- You cannot create children that have the same type as their parents -- for example, you cannot create a District under a parent that is also a District.
- The name of a group must be unique among its siblings -- for example, you cannot create two Institutions with the same name under the same District. (How could you tell them apart?!)
- Name length must be between 3 and 100.
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:
- Add a new credential
- Set the expiration (see next transaction) for the old credential for a week in the future
- Over the next couple days change and deploy your API and LTI interfacees to use the new credential.
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:
active
: You can flip it betweenfalse
andtrue
. This change will take effect immediately, minus a few minutes for the effect of caching.expires
: You can set it to some date in the future or clear it out.
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:
- A class and its associated LTI context identifier
- An instructor and its associated LTI user identifier
- A set of 15 students and their associated LTI user identifiers
- An assignment and its associated LTI resource link identifier
- One or more essays for every student.
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:
- An assignment and its associated LTI resource link identifier
- One or more essays for every student existing in the class.
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:
- submit a report, specifying criteria for data you want back
- poll for the report's completion (if appropriate), and
- download the report's results
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
- Errors: A report may fail during execution for some reason. If that happens
the
error
key in the returned report information will contain a brief description of the problem. You may also see a discussion of errors for HTTP status codes we use, and for the formats of error documents. - Caching: We cache report results for some period of time after they complete,
typically an hour. (You'll find the cache time in the
runtime.results_time_to_live_secs
path in the response.) The results are cached based on the criteria, so if you request multiple reports with the same criteria during the cache time you'll get the same results back for each. Also, if you need to extend that cache time please contact the Revision Assistant product team. - Data liveness: Data in the reporting system is not updated in real-time, so if you perform actions in RA and run a report immediately those actions won't be in the data. (The current update window is approximately every 10 minutes.)
Data formats
- JSON inbound,
- JSON outbound by default with an option for CSV,
- Timestamps are all UTC and in ISO 8601 format -- for example "2018-02-14T18:09:53Z" for "February 14, 2018 at 1:09:53 PM EST"
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:
report_type
: this is a unique identifier associated with each type of reportexternal_id
: an optional identifier that we'll also include in the response; this can make it easier to correlate requests to responses since they'll be processed asynchronouslycriteria
: one or more key/value pairs with search criteria, dependent on the report type. Every report should support:data_refreshed
: A timestamp we'll check against the last report data update. If the data hasn't been updated after this date then we'll return askip
response immediately and not even execute the report -- see below for an example. Note that if you poll the URL in the response you'll get a 404.
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:
report_type
: the type of report you requestedstatus
: the current statuscriteria
: the criteria you're filtering data by; appropriate criteria will depend on the type of reportexternal_id
: if provided in the request we'll echo this back to you in all responsesurl
: the URL you use to poll for results
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:
- started: When the report run started
- completed: When the report run completed
- elapsed_secs: Number of seconds elapsed (decimal to precision 3)
- data_refreshed: A timestamp when the data backing this report was last updated. It may or may not mean that the data in any particular report is stale, that depends on the report criteria. But it gives you a way to see (and possibly present to users) the freshness of the data backing the report.
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
draft_create_after
: a datetime after which drafts must have been createddraft_create_before
: a datetime before which drafts must have been createdinclude_draft_text
: iftrue
we'll include the text of each draft in the response; if you're returning a lot of data this will dramatically increase the size of the data returned, so only use this if you need it. (Defaults tofalse
.)instructor_lti_user_id
: one or more strings of LTI user IDs for assignment instructorsis_submission
: iftrue
orfalse
we'll only return drafts where the student did or did not click theTurn In
button in Revision Assistant. (Defaults to neither, which means both submissions and non-submissions will be in response.)last_draft_only
: iftrue
we'll only include the most recent draft for each student and assignment in the response. You can combine this withis_submission=true
to only return the last submission for each student and assignment. (Defaults tofalse
.)lti_context_id
: one or more strings of LTI context IDs (class identifiers)lti_resource_link_id
: one or more strings of LTI resource link IDs (assignment identifiers)resource_link_status
: one or more strings of assignment states you want to filter by; available options are:open
,closed
,archived
, anddeleted
student_lti_user_id
: one or more strings of LTI user IDs for student writers
JSON Results
Here are the top-level keys you'll find in results
; we'll give details of
each of them below:
assignments
: Zero or more assignments referenced in the listed draftsclasses
: Zero or more classes referenced in the listed draftsdrafts
: Zero or more drafts matching the given criteriainstructors
: Zero or more users referenced as the assignment or class creator of the listed draftsprompts
: Zero or more prompts referenced in the listed draftsstudents
: Zero or more users referenced as the author of the listed drafts
Assignments:
Each record in the assignments
array will be one referenced by a draft in the
resultset by drafts.assignment_id
.
assignment_type
: type of assignment, will always be "signal check"class_id
: internal class identifier, will reference a record inclasses.id
created
: timestamp when assignment was createdid
: internal identifierinstructor_id
: internal identifier for instructor who created assignment, will reference a record ininstructors.id
lti_resource_link_id
: external identifier for the assignment assigned during LTI launchname
: name given by user to assignmentprompt_id
: internal identifier for prompt associated with assignment, will reference a record inprompts.id
state
: current state of the assignment, one of:- archived
- closed
- deleted
- open
Classes:
Each record in the classes
array will be one referenced by a draft in the
resultset by drafts.class_id
.
created
: timestamp when class was createdid
: internal identifierinstructor_ids
: array of internal identifiers for instructors associated with class, will reference a record ininstructors.id
lti_context_id
: external identifier for the class, assigned during LTI launchname
: name given by user to classstate
: current state of the class, one of:- active
- archived
- inactive
student_ids
: array of internal identifiers for students associated with class, may reference a record instudents.id
if they wrote a draft
Drafts:
Each record in the drafts
array represents a draft written by a student.
answer_id
: internal identifier for draftassignment_id
: internal identifier for assignment, references record inassignments.id
assignment_type
: type of assignment, currently always "signal check"class_id
: internal class identifier, will reference a record inclasses.id
created
: itimestamp when draft was writtendistrict_id
: internal identifier for districtdraft_index
: count of this draft by this student on this assignment; the first draft written will be "1"draft_text
: text of draft, typically as HTML; this will only be included if you suppliedinclude_draft_text=true
in your report criteria.identity_assignment_creator_id
: instructor who created the assignment this draft was written for, referencesinstructors.id
identity_class_creator_id
: instructor who created the class in this assignment, referencesinstructors.id
is_submission
: true if this draft was sent with the 'Turn in' button, false of notis_valid
: true if this draft was valid for scoring -- that is, it was long enough, and on topicprompt_id
: internal identifier for prompt this draft was written against, referencesprompts.id
school_id
: internal identifier for school, may be nullscores
: object with scores this student received on the draft; the keys of the object will be the trait names, and the values will be the number of signal check bars the student saw (1-4)student_id
: internal identifier for student, referencesstudents.id
valid_draft_index
: count of this draft by this student on this assignment among the valid drafts; the first valid draft written will be "1"; will be empty if the draft wasn't validword_count
: number of words in the draft (naive counting)
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
.
additional_identifiers
: Zero or more additional identifiers that you've told us about in LTI launches. Each one has atype
andidentifier
field.created
: timestamp when instructor record was createddisplay_name
: name we show to userfirst_name
: first name of userid
: internal identifier for userlast_name
: last name of userlti_user_id
: external identifier for the user assigned during LTI launch or provisioning
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.
created
: timestamp when prompt was createdid
: internal identifier for promptgenre
: genre of promptgenre_slug
: unique short name for promptmin_grade_level
: lowest recommended grade levelmax_grade_level
: highest recommended grade levelrubric_file
: URL to rubric PDFslug
: short, unique, unambiguous, internal name of prompttitle
: displayable title of prompttraits
: array of trait names in Genre, typically four
Students:
Each record in the students
array will be one referenced by a draft in the
resultset by drafts.student_id
additional_identifiers
: Zero or more additional identifiers that you've told us about in LTI launches. Each one has atype
andidentifier
field.created
: timestamp when student record was createddisplay_name
: name we show to userfirst_name
: first name of userid
: internal identifier for userlast_name
: last name of userlti_user_id
: external identifier for the user assigned during LTI launch or provisioning
CSV Results
If you request a CSV the return data will include the following base values:
- Prompt ID: Internal identifier for prompt
- Prompt Slug: Consistent identifier for prompt, what you'll use in an LTI launch or when you create an assignment
- Prompt Title: User-visible title of prompt
- Prompt Genre: Genre of prompt
- Class ID: Internal identifier for class (context)
- Class Name: User-visible name for class (context)
- Class Created: Datetime class was created
- Class LTI ID: LTI context ID associated with class
- Assignment ID: Internal identifier for assignment (resource link)
- Assignment Name: User-visible name for assignment (resource link)
- Assignment Type: Type of assignment (resource link)
- Assignment Created: Datetime assignment was created
- Assignment LTI ID: LTI resource link ID associated with assignment
- Teacher ID: Internal identifier for instructor
- Teacher Name: Instructor name
- Teacher LTI ID: LTI user ID associated with instructor
- Student ID: Internal identifier for student
- Student Name: Student name
- Student LTI ID: Internal identifier for student
- Draft ID: Internal identifier for draft
- Draft Index: Which draft is this for the student and assignment?
- Draft Valid Index: Which valid draft is this for the student and assignment?
- Draft Word Count: Number of words in the draft
- Draft Is Submission: True if submission, False if not
- Draft Is Valid: True if valid, False if the draft triggered a validation message like 'Too short' or 'Off-topic'
- Draft Created: Datetime draft was created
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:
- Historical Analysis: Analysis and Evidence
- Historical Analysis: Claim and Focus
- Historical Analysis: Language and Style
- Historical Analysis: Organization
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:
- It's an API for scores only, no feedback
- It requires coordination with the Revision Assistant product team to setup the class, assignment, and user beforehand.
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:
- Essay too short: if an essay is obviously too short you'll get back a
400
status along with a message in thetext
field - Essay too long: if an essay is obviously too long you'll get back a
400
status along with a message in thetext
field - Essay has one or more words that are too long: if any word in the essay is
obviously too long you'll get back a
400
status along with a message in thetext
field - Missing
assignment_id
: if the essay is missing theassignment_id
attribute - Assignment not found: if you specify an
assignment_id
that doesn't belong to your account, or that's not been marked as a TSE assignment. Restrict the IDs you use to the values returned by/tse/prediction/assignments
.
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.