Locations
A location is one thing in your tool an instructor can put in a course — a quiz, a chapter, a lab, a practice set. When an instructor adds your tool to a course, they choose a location, and every launch from that link tells your tool which one it is for.
Every tool starts with the home location. Tools that offer multiple activities should define additional locations. Each additional location corresponds to a "thing" in your tool, regardless of how many times it is "placed" in the tool. Voshi does its best to enforce one link per location, but ultimately, the LMS is the final authority on its links. The rules for link duplication and semester rollover of courses varies with each LMS. Therefore, the locations you define in Voshi--regardless of where linked in an LMS--constitute uniqueness for your tool. Two links that go to the same location launch with the same extid, share the same storage, and feed the same gradebook column.
If you haven't read Concepts yet, start there. It defines locations, extids, and placements, and explains why the extid you choose is the most consequential decision you make.
What a location is made of
| Field | Meaning |
|---|---|
External ID (extid) | Your tool's own name for the location: home, ch3_quiz, lab-2. Letters, digits, dashes, and underscores. Permanent: links placed in courses and the data students saved there are bound to it, and it can never be changed. |
| Label | What the instructor sees in the picker, and the default name of the link they place. |
| Description | Optional. Shown in the picker. |
| Type | What the location is for — see below. |
| Points | For an assessment: what the gradebook column is worth when an instructor places it. 100 if nothing says otherwise. The AI Builder records it when it creates a graded location, and the Locations tab shows it under the type. Your tool should send the same number as maxScore when it reports a grade. |
| Voshi ID | Voshi's own identifier for the location, assigned when it is created. Starts with I. Useful for matching the dashboard's logs; your code uses the extid. |
Types
| Type | Meaning |
|---|---|
assessment | Graded work. The only type that can report a grade. Placing it creates a gradebook column. |
practice | Student practice; no grade attached. |
content | Reading or reference material. |
setup | A configuration screen for the instructor, not a student-facing activity. |
External ids are permanent
The extid is the one part of a location that is yours--and the one part that can never change. Plan your extid scheme carefully before you create the first one.
An extid is how your tool knows the location. Each extid pairs with exactly one Voshi location, and Voshi finds a location by tool ID plus extid. The Voshi ID is for Voshi's own bookkeeping and for matching the dashboard's logs; your code never routes on it. When your tool launches, the first thing it should do is read api.location.extid, because that value decides everything else: which assignment to show, which mode to run in, which route to take. The pattern is in Handling locations on launch.
The format is entirely up to you. An extid is a string of letters (in any language), digits, underscores, and dashes, and nothing else: no spaces, dots, slashes, or other punctuation. Case matters, so Quiz1 and quiz1 would be two different locations. Keep extids short and poignant. An extid appears in every launch, in your switch, in the dashboard, and in the logs, and well-planned, concise extids are easier to live with than a clumsy scheme.
Once an extid exists, it is bound to its location forever. It cannot be renamed, and it cannot be deleted and used again for something else. Discontinuing a location keeps the extid, and reactivating it brings back the same location with the same Voshi ID. The links instructors have placed, the storage rows, and the gradebook columns all hang off the extid, so an extid must exactly identify the thing that was launched, in every course, this term and every term after it.
Keep the course out of the extid. An extid should say nothing about the school, the course, the instructor, the student, the year, or the term. Every launch already carries all of that (api.organization, api.course, api.user; see Launch properties), and an extid that repeats it ties a location to one course when it should serve every course that ever places it. quiz1 is the same quiz in every section, every semester, at every school.
Never rename an extid. A renamed extid is a new, empty location, and the old one is retired with every link and each student's data still bound to it. An proper extid is ch3_quiz1. A problematic one is fall2026_cs101_quiz3 because course, term, and person already arrive separately in every launch.
Example schemes
How many extids a tool needs, and what they look like, follows from what the tool offers.
Defining locations
The rule: one location for each thing you want graded, and one for each non-graded thing you want a student to be able to open. A location's grades land in one gradebook column, so two graded activities need two locations.
A location is a link in the course. Each location becomes one link where the instructor places it, so a tool with ten extids should expect to be placed up to ten times in a single course, one link per location. Don't plan on an instructor placing the same location twice to get two of anything: both links point at the same activity, share the same storage, and feed the same gradebook column.
Two links to one extid are still one location. Duplicate links happen anyway, when an instructor copies an existing link, a course is rolled forward, or a module is rebuilt, and your tool is never told. Treat them as what they are: two ways into the same resource. Every launch through either link carries the same extid, both read and write the same storage rows, and a grade always posts to the first link's gradebook column, whichever link the student came through. Your code needs no special handling for them.
Each location has its own storage in each course. Two of the four storage scopes are keyed on the location, so every launch arrives with data that belongs to this extid in this course and no other:
| Scope | Keyed on | Holds |
|---|---|---|
api.storage.location | location × course | The instructor's setup of this activity in this course: the word list, the due date, the question pool. |
api.storage.memberLocation | person × location × course | One student's work on this activity: answers, attempts, the saved draft. |
Since the extid is what keys these rows, one extid per activity is also what keeps one activity's data separate from another's.
Locations are defined on the Locations tab of the dashboard. They belong to the tool itself and are not specific to any specific version: every course runs the version it adopted, and all of them place links from the same list.
Add Location creates one: name, external ID, and type. The external ID and the type cannot be changed afterwards; Edit changes the name. Every tool starts with home.
The AI Builder keeps the list in step with what it builds. Every build sees the tool's current locations and carries them forward — same extids, same points — and changes the list only when the conversation changed what the tool offers: a new activity is added, and a relabelled or regraded one is updated. A build never retires a location: one it no longer handles stays offered, and the build's note names it so you can discontinue it yourself once no course needs it (below). Most builds change nothing. Whatever a build changes is listed in its note in the conversation, and the rows are yours to edit afterwards on the Locations tab. A build may also file locations into named, collapsible groups, which the picker shows exactly as it does a external tool's; grouping is presentation only and never appears in a launch.
Handling locations on launch
Every launch says which location it is for: api.location.extid. A tool with several activities switches on it:
connectedCallback() {
switch (this.api.location.extid) {
case 'ch3_practice': this.renderPractice(); break
case 'ch3_quiz': this.renderQuiz(); break
default: this.renderUnknown() // a retired location, or one the code doesn't know yet
}
}
The launch also carries the location's id, type, and label, as Voshi recorded them when the link was placed. Nothing else: there is no per-location configuration object. If an instructor needs to configure an activity, keep that in api.storage.location, which is theirs to write and everyone's to read.
Retiring a location
Discontinue this location, in the location's Edit drawer, retires it: instructors are no longer offered it, and it moves to the Discontinued Locations list. Only you can do this — a build never retires anything, because a course running an older version of your tool may still be placing the location. Links already placed keep working, with their data and grades, and your code must go on handling the extid — courses are copied forward term after term, a course on an older version may still be placing it, and a link can outlive your catalog by years. Reactivate it at any time and it comes back with its original Voshi ID. A location is never deleted, and home cannot be discontinued.
In the picker
When an instructor adds your tool to a course, Voshi shows them your offered locations — labels and descriptions, groups collapsed — and they choose one or several. Placing an assessment creates the gradebook column, worth its points. Voshi copies the location's label and type into the link at that moment; a later edit to the label applies to new placements, while existing links keep what they were placed with.