Skip to main content
Version: 2026-09-21 (current)

Locations

Locations are the "things" your tool provides that an instructor can place in a course — a chapter quiz, a practice set, a reading, a setup screen. Your tool owns them: you identify each one with an external ID (extid) of your choosing, and Voshi asks your tool for the current list whenever an instructor is placing content.

Nothing about your catalog is stored in Voshi ahead of time. Add a chapter and it's placeable immediately; the list you serve is the list instructors see.

note

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 in your integration.

How instructors place your locations​

The instructor adds your tool to a course

From inside their LMS. Voshi handles the LMS side of this.

Voshi asks your tool what it offers

A server-to-server POST to your Locations URL, which answers with your destinations keyed by extid. See Serving your locations.

The instructor picks one

They see your label and description, choose a destination, and confirm. For assessment locations, a gradebook column is created at the same time.

Voshi provisions the placement

Voshi calls your Provision URL for the course and the location so your tool can set them up before any student arrives — see How provisioning works.

Every launch of that placement then carries the location's configuration to your launch receiver:

"location": {
"id": "Il0c8n",
"extid": "chapter3_quiz",
"type": "assessment",
"label": "Chapter 3 Quiz"
}

Voshi keeps its own copy of each destination's type, label, description, and points, taken from your list at the moment someone places it. That copy — one per extid, shared by every placement of it — is what a launch carries. (description and points are used by Voshi and the LMS; the launch claim carries type and label.)

warning

So your served list is not a live configuration channel for content that's already placed. Change a label and existing placements keep launching with the value captured when they were made, until that destination is placed again by anyone. Switch on location.extid and read the current configuration from your own data instead of relying on the claim to be current.

The two IDs​

Every location has both, and both arrive in the launch:

FieldWhose it isUse it for
location.extidyours, from the list you serveLooking the destination up in your own data. This is the one to switch on.
location.idVoshi's, minted the first time it sees your extidCorrelating with Voshi's dashboard logs. Starts with I and is unique across every tool, school, and course in the Voshi ecosystem.

The pairing is permanent, which is what lets an instructor's placement keep working across terms.

What makes a good extid​

An extid names a resource, not an occasion on which someone used it. That single rule produces the rest:

  • The same across courses, terms, and schools. If the Persian architecture quiz is quiz1 in one course, it is quiz1 in every other course, in every semester, at every institution. That's what lets a course copied into next term keep working, and what lets your tool find the student's history for a resource.
  • No course-specific identifiers. Don't build a context id, semester code, section number, term, or instructor id into an extid. quiz1 — not fall2026_cs101_quiz1. Course, term, and person already arrive separately in the launch; folding them into the extid multiplies your catalog and breaks it on the first course copy.
  • Letters, digits, dashes, and underscores only. No other special characters. Don't make one entirely of digits: browsers reorder all-numeric keys, so 1, 2, 10 will not reach the instructor in the order you served them. q1, unit-1, or any non-digit character avoids it.
  • Short and concise. There's no hard length limit, but the extid appears in your own data, in storage scoping, and in gradebook routing — keep it readable.
  • Permanent. Once an extid has been linked into a course, it can never be changed or reused for other content.
warning

Plan your extid scheme before you ship. Because extids are permanent once linked, changing your mind later means stranding every link instructors have already placed. Work through resource identity, provisioning in your own database, course duplication across semesters, grade passback and gradebook line items, and data-storage scoping first. See Extid for worked examples of static and dynamic extid strategies.

Types​

Each location declares what it's for:

TypeMeaning
assessmentGraded work. The only type that can pass a grade back. Placing it creates a gradebook column, worth the points you declared (100 by default), and enables the grades API for its launches.
practiceStudent practice; no grade attached.
contentReading or reference material, no submission.
setupA configuration screen, not a student-facing activity.

There are no per-location params​

A location carries no static key-value pairs. Everything a launch tells you about the destination is its extid, id, type, and label — there is no params object on the location, in the launch claim or in your locations response.

warning

If you built against an earlier version of these docs that described location.params, that object was never sent. Reading location["params"] raises; reading it defensively always yields nothing. Encode the configuration in the extid, or look it up in your own database.

So per-destination configuration lives in one of two places:

  • In the extid itself, when the variants are few and stable — ch1_quiz, ch2_quiz, ch3_quiz.
  • In your own data, keyed on the extid — the general answer, and the only one that lets you change configuration without re-placing anything.

Choosing a location strategy​

Two workable patterns:

  • One extid per destination (ch1_quiz, ch2_quiz, …) — the instructor's picker lists everything explicitly; your tool switches on location.extid.
  • A generated extid per placement — for tools where each link is its own distinct thing, like a per-lecture discussion. Generate a fresh extid (a uuid4 works) each time a link is placed, so the tool accumulates many dynamic locations rather than a fixed catalog.

Either way your tool switches on location.extid and reads the rest from its own records. See the examples in Concepts.

One location per thing​

Whichever pattern you pick, the sizing rule is the same: one location for each thing you want graded, and one location for each non-graded thing you want to show. If it needs its own gradebook column, it's its own location.

Don't plan on placing the same location twice in a course to get two of anything — both links resolve to the same resource, share the same storage rows, and feed the same gradebook column. Duplicate links do happen anyway (course copies, module edits), and your tool needs no special handling for them. See Placement.

warning

Keep serving your locations. Dropping an extid from the list you serve removes it from the instructor's picker, but every link already placed keeps launching — potentially for years, in courses copied forward term after term. Retiring a location breaks those courses, and nothing tells you it happened. Plan to serve a location for as long as anyone might still be linking to it. If you genuinely must stop offering one, keep handling its launches and render your own "no longer available" screen rather than erroring.

The dashboard's Locations tab​

For a self-hosted tool, the Locations tab is a read-only record: it lists the locations Voshi has seen, meaning the ones instructors have actually placed. A location shows up there the first time someone places it — not when you start offering it — so an empty tab on a new tool is normal, and the tab is never the list instructors choose from.

There is nothing to define there and nothing to keep in sync. The list you serve from your Locations URL is what an instructor sees.

note

Locations are defined in the dashboard for hosted tools, which have no server of their own to ask — see Locations in the hosted section. If you were told to create locations there and you run your own server, you're reading a stale instruction — build the endpoint instead.