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

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 carry description, locations, and its own grouped_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.params does not exist and never did on this contract. Earlier pages showed a params object on the launch and provision location claim, 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 reading location["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, and user.email are populated when groups contains manager, instructor, or assistant, and are null otherwise. Earlier pages said flatly that no PII is forwarded. Student launches are unchanged — still no PII. See user.
  • 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 context and location rows — they have read access only, and a write returns 403. Earlier pages warned that students could overwrite them.
  • Course staff can read and write any member's member and member_location rows 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 /grades accepts max_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_url and locations_url are readable and writable through GET/PATCH /apps/me, and settable in the dashboard's Profile tab. Earlier pages said to ask the MyEducator team.
  • POST /apps/me/rotate-key issues a new API key and invalidates the old one — see Rotate your API key.
  • The legacy location object is {id, extid, type, label}, and extid is required when creating one. Earlier pages showed is_default, order, and params instead.

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) and location.id (Voshi's). Switch on extid.
  • The dashboard's Locations editor and the apps/me/locations API 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 204 promptly, then PUT each URL in the provision claim when that level is ready — see How provisioning works.
  • The context and location storage rows now answer 422 … has not been provisioned until you finish. member and member_location are 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​

BeforeNowWhat to do
user.role (student / instructor / admin)user.groups (a list) and user.memberReplace role comparisons with membership checks ("instructor" in groups). Key enrollment records on user.member. See user.
resource_link_idresource_linkRename. Same meaning.
organization.idorganization.deploymentRename — deployment always held the same value.
—contextNew: the course ID on its own, matching the storage URLs and used by provisioning. course still carries the display text.
—location.extidNew: your own ID for the launched destination.

Corrections to these docs​

Behavior that didn't change, but that earlier pages described wrongly:

  • iss is 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 showed link.voshi.com.
  • A launch's location values 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 off location.extid and your own data.

Migration checklist​

  1. Replace user.role reads with user.groups membership checks; store groups against user.member.
  2. Rename resource_link_id → resource_link and organization.id → organization.deployment.
  3. Build and register your Locations URL — without it your app can't be placed.
  4. Build and register your Provision URL, and call the provision URLs when setup finishes.
  5. If you relied on Voshi copying course data across terms, move that into your provisioning handler.
  6. If you pinned iss to the LMS issuer, pin it to https://api.link.voshi.com instead.