Changelog
Changes to what Voshi sends your app or expects from it, newest first. Each entry says what you have to change to keep working.
The docs as they stood before each change stay published — pick the date from the version dropdown at the top of any page, or follow the links below.
2026-09-04 — Messages and grouped locations
No action required. Both additions are optional, and an app that answers the locations request exactly as it does today keeps working unchanged. No snapshot was cut: nothing about the old contract stopped being true.
Your locations response can carry a message
Send a message alongside (or instead of) your locations and Voshi shows it to the instructor above the picker. It is the answer to the empty picker: when you offer nothing, you are the only party who knows why — an email that matches no account, an institution that has adopted nothing — and until now there was no way to say so.
- Shown as plain text; markup is stripped, and it is truncated at 2000 characters.
- Valid on its own:
{"message": "…"}with no locations is a complete answer. - See
message.
Your locations can be grouped
A new grouped_locations key arranges locations into named, collapsible sections, nested up to 5 levels. Instructors see them collapsed, open what they want, and can take a whole group at once or pick from several groups in one placement.
- A group needs a
label, and it has to be a real one: an empty label, or one that is only whitespace or markup, is refused. It may also carrydescription,locations, and its owngrouped_locations— the same key again, since a group holds locations the way the response itself does. - Grouping is presentation only — it is not part of an extid, is not stored on the location, and never appears in a launch, so you can reorganize freely without disturbing content that is already placed.
- Locations sent at the top level are shown above the groups, uncollapsed.
- See
grouped_locations.
One thing that is now refused
A locations response naming none of locations, grouped_locations, or message is rejected as an invalid structure. Previously locations was required, so a response without it was already refused; this only changes the wording of the error. To answer "nothing right now", send a message, an empty locations object, or both.
2026-09-02 — Documentation corrections
No contract change. Nothing about what Voshi sends or accepts changed on this date; these are places where the docs described the platform wrongly. No snapshot was cut, because there is no earlier contract to strand anyone on. Check each item against your integration.
Claims
location.paramsdoes not exist and never did on this contract. Earlier pages showed aparamsobject on the launch and provisionlocationclaim, documented it as a per-location field on your locations response, and suggested a "few parameterized destinations" strategy built on it. Voshi sends no such object and stores no such field. If you wrote code readinglocation["params"], it has been failing or silently reading nothing — move that configuration into the extid or your own database. See There are no per-location params.- Launches to course staff carry name and email.
user.given_name,user.family_name,user.full_name, anduser.emailare populated whengroupscontainsmanager,instructor, orassistant, and arenullotherwise. Earlier pages said flatly that no PII is forwarded. Student launches are unchanged — still no PII. Seeuser. - A provision request does carry
course. It was listed among the claims a provision request does not have. It has always been sent, in the same shape as a launch's.
App data permissions
The who-can-access-what rules were wrong in both directions:
- Students cannot write the shared
contextandlocationrows — they have read access only, and a write returns403. Earlier pages warned that students could overwrite them. - Course staff can read and write any member's
memberandmember_locationrows in their course. Earlier pages said only the user themself could reach their own rows. - App builders get no automatic access to any row. Being a member of the app in the dashboard grants nothing; a builder has to be enrolled in the course to read data there.
APIs
POST /gradesacceptsmax_score, which rewrites the gradebook column's points possible before publishing the score, and returns it on the grade object. Undocumented until now — see the request body.provision_urlandlocations_urlare readable and writable throughGET/PATCH /apps/me, and settable in the dashboard's Profile tab. Earlier pages said to ask the MyEducator team.POST /apps/me/rotate-keyissues a new API key and invalidates the old one — see Rotate your API key.- The legacy location object is
{id, extid, type, label}, andextidis required when creating one. Earlier pages showedis_default,order, andparamsinstead.
Dashboard
The app detail tabs are Overview, Profile, Locations, Test, Launches, Grade Logs. Pages referring to a Settings tab meant Profile, and there is no Members tab — ask the MyEducator team to change who manages an app.
2026-08-25 — Provisioning and app-served locations
Action required. Your app now builds three endpoints instead of one, and one claim was replaced. The contract before this change is archived at 2026-08-19.
New: your app serves its own locations
Locations are no longer defined in the dashboard. Voshi asks your app for its destinations whenever an instructor is placing content, and you identify each one with an external ID of your choosing.
- Register a Locations URL and answer it with
{"locations": {"<extid>": {…}}}— see Serving your locations. - An app with no Locations URL cannot be placed in a course. This is the change most likely to stop a working integration.
- Launches now carry both
location.extid(yours) andlocation.id(Voshi's). Switch onextid. - The dashboard's Locations editor and the
apps/me/locationsAPI still exist but no longer drive what instructors see.
New: provisioning
Before the first launch of a course reaches you, Voshi asks your app to set that course up.
- Register a Provision URL, answer
204promptly, thenPUTeach URL in theprovisionclaim when that level is ready — see How provisioning works. - The
contextandlocationstorage rows now answer422 … has not been provisioneduntil you finish.memberandmember_locationare unaffected. - Voshi no longer copies course data into a copied course. A provision request for a copied course hands you the previous course's row URLs (
storage.parent_context,storage.parent_location) and you carry forward what matters — see Copied courses.
Changed claims
| Before | Now | What to do |
|---|---|---|
user.role (student / instructor / admin) | user.groups (a list) and user.member | Replace role comparisons with membership checks ("instructor" in groups). Key enrollment records on user.member. See user. |
resource_link_id | resource_link | Rename. Same meaning. |
organization.id | organization.deployment | Rename — deployment always held the same value. |
| — | context | New: the course ID on its own, matching the storage URLs and used by provisioning. course still carries the display text. |
| — | location.extid | New: your own ID for the launched destination. |
Corrections to these docs
Behavior that didn't change, but that earlier pages described wrongly:
issis always Voshi (https://api.link.voshi.com), not the school's LMS. If you configured your JWT library to expect the LMS issuer, it was never matching what we send.- Storage URLs are on
api.link.voshi.com. Examples showedlink.voshi.com. - A launch's
locationvalues are the copy Voshi captured when the placement was made, not your current list. Earlier pages said edits applied immediately to existing placements. Drive your app offlocation.extidand your own data.
Migration checklist
- Replace
user.rolereads withuser.groupsmembership checks; store groups againstuser.member. - Rename
resource_link_id→resource_linkandorganization.id→organization.deployment. - Build and register your Locations URL — without it your app can't be placed.
- Build and register your Provision URL, and call the provision URLs when setup finishes.
- If you relied on Voshi copying course data across terms, move that into your provisioning handler.
- If you pinned
issto the LMS issuer, pin it tohttps://api.link.voshi.cominstead.