Skip to main content
Version: 2026-08-25 (archived)

Launch Data reference

After verification, the launch_data JWT decodes to a flat set of claims:

{
"iat": 1751470000,
"exp": 1751477200,
"iss": "https://api.link.voshi.com",
"api": {
"domain": "api.link.voshi.com",
"token": "8f14e45fceea167a5a36dedd4bea2543"
},
"launch_id": "I9gbX9ExUrt6",
"time": "2026-07-27T18:03:00+00:00",
"app": "I4ppXy",
"user": {
"id": "In4kDp7yZq",
"member": "Im8mBr42",
"groups": ["student"],
"given_name": null,
"family_name": null,
"full_name": null,
"email": null
},
"course": {
"id": "IcT91mBxze",
"name": "Intro to Computing",
"label": "CS101"
},
"context": "IcT91mBxze",
"organization": {
"title": "Example University",
"issuer": "I1ssU3r",
"client": "Icl13nt",
"deployment": "Id3pL0yMnt"
},
"resource_link": "Ir3sLnk42",
"location": {
"id": "Il0c8n",
"extid": "quiz1",
"type": "assessment",
"label": "Quiz 1"
},
"grade_passback": true,
"storage": {
"context": "https://api.link.voshi.com/lti13/v1/contexts/IcT91mBxze/apps/I4ppXy/data",
"location": "https://api.link.voshi.com/lti13/v1/contexts/IcT91mBxze/apps/I4ppXy/locations/ext:quiz1/data",
"member": "https://api.link.voshi.com/lti13/v1/contexts/IcT91mBxze/members/Im8mBr42/apps/I4ppXy/data",
"member_location": "https://api.link.voshi.com/lti13/v1/contexts/IcT91mBxze/members/Im8mBr42/apps/I4ppXy/locations/ext:quiz1/data"
}
}
note

All IDs are Voshi's own IDs — stable across launches, but never the LMS's internal IDs. The raw LMS user identifier is not available to your app. Student launches carry no PII: name and email are null unless the launching user is course staff. Recognize a returning user by user.id, not by anything else.

Claims​

iatinteger

Standard JWT "issued at" — Unix epoch seconds (UTC) when the token was signed. Use the time claim, not iat, if you want a human-readable launch timestamp.

expinteger

Standard JWT expiry — Unix epoch seconds. Currently iat + 2 hours. Your JWT library checks this during verification and rejects an expired token.

issstring

Voshi — always the fixed string https://api.link.voshi.com (the https:// form of api.domain). It is not the school's LMS; that's organization.issuer. Since Voshi signs every launch_data, you can configure your JWT library to require this exact issuer alongside the signature check.

apiobject

The credential for calling the App Data API as the launching user.

api fields
domainstring

The API host to call, e.g. api.link.voshi.com.

tokenstring

A bearer token tied to the launching user's session: send it as Authorization: Bearer <token>. It expires with the session (about a week), so treat it as per-launch — get a fresh one from the next launch rather than storing it long-term. Keep it server-side; it acts as that user.

launch_idstring

The ID of this launch. Required to send a grade later — store it.

timestring

When the launch happened, as an ISO 8601 timestamp.

appstring

Your app's ID.

userobject

Who launched, and what they are in this course.

user fields
idstring

The person's stable Voshi ID — the same across every launch by them, in any course at the same school.

memberstring

Their membership ID: this person in this course. It's the natural key for an enrollment record, and it's the same ID that appears in the members/… segment of the storage URLs below. A person in two courses arrives with one user.id and two user.members.

groupsarray

What they are in this course, as a list of role groups:

GroupWho
managerCourse or account manager, and institution/system administrators.
instructorThe instructor of record, faculty, lecturer.
assistantTeaching assistant, content developer, course staff.
mentorMentor or observer — someone watching a student's progress rather than doing the work.
studentLearner.

Check membership, not equality ("instructor" in groups) — a person can hold more than one group, and the list can grow. Roles the LMS sends that Voshi doesn't recognize arrive as ["student"], so student is also the least-privilege default and groups is never empty.

It is recalculated on every launch from the roles the LMS sent, and it is per-course: the same user.id is legitimately a student in one course and an instructor in another, and either can change between launches. Store it on the membership, never on the person, and refresh it every time.

given_namestring | null

Course staff only. The launching user's first name, or null.

family_namestring | null

Course staff only. Their last name, or null.

full_namestring | null

Course staff only. Their display name, or null.

emailstring | null

Course staff only. Their email address, or null.

warning

These four fields are populated only when groups contains manager, instructor, or assistant — so you can address an instructor by name and email them about their course. For everyone else, including student and mentor launches, all four are null. Never build a flow that needs a name or an email from a student launch, and never use them to identify a returning user — that is user.id's job.

courseobject

The course the launch came from.

course fields
idstring

The course's stable Voshi ID.

namestring

The course title, e.g. Intro to Computing.

labelstring

The course's short label, e.g. CS101.

contextstring

The same course, as the bare ID — it matches the contexts/… segment of the storage URLs and is the ID used by provisioning. course carries the display text; context is the identifier.

organizationobject

The school.

organization fields
titlestring

The school's display name, e.g. Example University.

issuerstring

Voshi's ID for the LMS platform (e.g. one Canvas cloud). Most apps can ignore this.

clientstring

Voshi's ID for the LMS registration under that platform. Most apps can ignore this.

deploymentstring

Voshi's ID for the school's deployment — the most specific of the three, and the one to treat as "which school is this". Use it as your school key.

resource_linkstring

Voshi's ID for the specific link in the course, or "" if the link was never created through the content picker. Don't use this to identify your resource — use location.extid, which names the same "thing" in every course and every term. See Placement.

locationobject

Which of your locations was launched.

location fields
idstring

Voshi's ID for the location, minted the first time it saw your extid.

extidstring

Your own external ID for this destination — the key you served from your locations endpoint. This is the one to switch on, and the one to key your own data on. See Extid.

typestring

One of assessment, practice, content, or setup. Only assessment locations can pass grades back.

labelstring

The label you gave the location, e.g. Quiz 1. Display text only — it's the copy Voshi captured when the placement was made.

grade_passbackboolean

true only when this launch can accept a grade: the location is of type assessment and the LMS created a gradebook column for the placement. If false, any grade you submit for this launch is rejected. Note that an assessment location can still launch with false — typically when the placement was created without the content picker, so no column exists.

storageobject

URLs of your four storage rows for this launch — read and write them through the App Data API. Each value is the row's full URL: call it exactly as given, and treat it as opaque. Don't parse it, and don't build these URLs yourself — the path shape is not part of the contract and the segments are not always what you'd guess. (The location segment is normally ext:<your extid>, but it falls back to Voshi's internal location ID for a location that has no extid; the members/… segment is user.member.)

storage fields
contextstring

Data row for this course. Closed until the course is provisioned.

locationstring

Data row for this location in this course. Closed until the location is provisioned. Note the scope: it belongs to the extid, so it is shared by every link in the course that points at this location — see Placement.

memberstring

Data row for this user within the course. Open from the first launch.

member_locationstring

Data row for this user on this location — e.g. their submission state. Open from the first launch.

What is deliberately absent​

  • Name and email on a student launch — the four user PII fields are null for anyone who isn't manager, instructor, or assistant.
  • The LMS's user ID — not available to your app.
  • LTI custom parameters from the LMS placement — not forwarded. Look the destination up in your own data, keyed on location.extid, for per-placement configuration.
  • location.params — locations carry no static key-value pairs. Earlier docs described a params object on the location claim; it is not sent, and never was on this contract. Key your configuration on location.extid instead.
  • user.role — replaced by user.groups, which says the same thing with more precision. If your app reads role, switch it to a groups membership check.