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"
}
}
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
iatintegerStandard 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.
expintegerStandard JWT expiry — Unix epoch seconds. Currently iat + 2 hours. Your JWT library checks this during verification and rejects an expired token.
issstringVoshi — 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.
apiobjectThe credential for calling the App Data API as the launching user.
launch_idstringThe ID of this launch. Required to send a grade later — store it.
timestringWhen the launch happened, as an ISO 8601 timestamp.
appstringYour app's ID.
userobjectWho launched, and what they are in this course.
courseobjectThe course the launch came from.
contextstringThe 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.
organizationobjectThe school.
resource_linkstringVoshi'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.
locationobjectWhich of your locations was launched.
grade_passbackbooleantrue 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.
storageobjectURLs 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.)
What is deliberately absent
- Name and email on a student launch — the four
userPII fields arenullfor anyone who isn'tmanager,instructor, orassistant. - 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 aparamsobject on the location claim; it is not sent, and never was on this contract. Key your configuration onlocation.extidinstead.user.role— replaced byuser.groups, which says the same thing with more precision. If your app readsrole, switch it to agroupsmembership check.