# Voshi Developer Docs (complete)
> Voshi connects educational tools to Learning Management Systems (Canvas, Blackboard, Moodle, and other LTI 1.3 platforms). Most tools are hosted by Voshi: a web component built in the dashboard or uploaded, which receives a VoshiApi object carrying the launch, storage, files, and grade passback. A self-hosted tool instead receives launches as a signed JWT POSTed to its own HTTPS endpoints and calls the REST APIs directly.
# Welcome to Voshi
> Build an educational tool once, and reach every LMS through the Voshi platform.
Source: https://myeducator-llc.github.io/voshi-docs/
Voshi connects your educational tool to Learning Management Systems — Canvas, Blackboard, Moodle, and any other LTI 1.3 platform — without you writing a single line of LTI code, and without you running a server.
You describe the tool you want in the dashboard's **AI Builder**, or upload code you wrote yourself. Voshi hosts it, puts it in front of instructors, launches it inside their courses, keeps each student's work, and sends scores to the gradebook. Your tool is a small piece of browser code that talks to Voshi through one object, the **Voshi API**.
Voshi is in early stages of development. These docs and the related features can and probably will change — we'll keep you appraised of changes that impact you.
**Building with an AI assistant?** The entire documentation is available as a single markdown file at [https://myeducator-llc.github.io/voshi-docs/llms-full.txt](https://myeducator-llc.github.io/voshi-docs/llms-full.txt) — point your assistant at that URL and it has everything. A page index lives at [/llms.txt](https://myeducator-llc.github.io/voshi-docs/llms.txt).
## How it works
1. **You register your tool** in the [Voshi dashboard](https://zen.voshi.com/app/ltiaas/s/) — a name and a description.
2. **You build it.** Tell the AI Builder what the learner should be doing and it writes the tool; or write the code yourself and upload the folder. Either way, the result runs immediately in the dashboard's **Sandbox**, a practice course where you can open your tool as a student or as an instructor.
3. **You add locations.** A *location* is one thing in your tool an instructor can put in a course — a quiz, a chapter, a lab. A tool with a single activity has a single location.
4. **You publish.** Publishing makes a numbered version of your tool that courses can adopt.
5. **An instructor adds your tool** to a course. They pick one of your locations, and Voshi creates the link (and the gradebook column, for graded work).
6. **A student opens it.** Voshi loads your tool inside the course page and hands it the Voshi API: who launched, which location, and where to save their work.
7. **Your tool reports a score** (optional) with one call. Voshi puts it in the gradebook.
```mermaid
sequenceDiagram
autonumber
participant LMS
participant Voshi
participant Tool as Your tool (in the browser)
LMS->>Voshi: Instructor places one of your locations
LMS->>Voshi: Student opens the link
Voshi->>Tool: Load your files, construct your element with the Voshi API
Tool->>Voshi: Read and save the student's work (api.storage)
Tool->>Voshi: Report a score (api.submitGrade)
Voshi->>LMS: Sync the score to the gradebook
```
## What Voshi gives your tool
Everything arrives on one object, `api`, that Voshi passes to your tool when it starts:
- **Who launched**: a stable user ID, whether they are a student or course staff, and their name and email when they are staff. Students are anonymous to your tool.
- **Where**: the course and the school.
- **What**: which of your locations was opened.
- **Storage**: four places to keep JSON data and files — per course, per location, per student, and per student-at-a-location. No database to set up.
- **Grades**: `api.submitGrade()`, available whenever the launched location has a gradebook column.
## Start building
Register a tool, build it in the AI Builder, run it in the Sandbox, and publish.
Locations, extids, and placements — the vocabulary everything else uses.
What your code is, what Voshi does for it, and the rules it lives by.
The one object your tool talks to: launch details, storage, files, and grades.
## Running your own server instead
Most tools are hosted by Voshi. If your tool needs its own backend — a database of its own, server-side grading, an existing product you are connecting — Voshi can hand launches to your server over HTTPS instead. That is a different integration, with endpoints to build and a signed token to verify. It is covered in [Advanced: your own server](https://myeducator-llc.github.io/voshi-docs/advanced/overview), and the choice is made once, when you register the tool.
---
# Quickstart
> Build your first Voshi tool: register, describe it to the AI Builder, run it in the Sandbox, and publish.
Source: https://myeducator-llc.github.io/voshi-docs/quickstart
This walkthrough takes you from nothing to a tool that instructors can place in a course. You will not write a server, configure a web host, or handle a login — Voshi does all of that. What you write (or have the AI write) is the tool itself.
## What you'll need
- A Voshi builder account — contact the MyEducator team if you don't have dashboard access yet.
- An idea of what the learner should be doing. That is the whole brief.
Open the [Voshi dashboard](https://zen.voshi.com/app/ltiaas/s/), go to **Tools → New Tool**, and fill in:
- **Tool Name** — shown to instructors when they add your tool to a course.
- **Hosting** — leave it on **Voshi Hosted**. This is the choice these docs describe, and it cannot be changed later. (**Self Hosted** means you run your own server; see [Advanced: your own server](https://myeducator-llc.github.io/voshi-docs/advanced/overview).)
- **Description** — optional, but instructors see it.
Your new tool opens on its **AI Builder** page.
In the **Discuss Design** panel, say what the learner should be learning or doing. Write it the way you would explain it to a colleague:
> A flashcard drill for ten accounting terms. The student flips each card, marks whether they knew it, and sees a score at the end. The score goes to the gradebook.
The AI replies with questions or with a plan. Answer what matters to you; it fills in the rest. When you are ready, press the **build** button (the wand). Building takes a few minutes.
When the build finishes, the **Running Tool** panel on the right launches your tool for real, in your tool's private **Sandbox** course. Pick a location and a role — **student** or **instructor** — and use the tool exactly as a learner would.
This is not a preview. The tool is saving data, checking roles, and reporting scores against a real course, so what works here works in a school's LMS.
Keep talking in the chat. "Make the cards bigger." "Add a timer." "Let the instructor change the terms." Each build replaces your tool's **draft**, and the Running Tool panel reloads it.
You can also take the code elsewhere: **Download Code** gives you the folder, including an `AGENTS.md` that briefs your own coding assistant on the rules, and **Upload Code** puts your edited folder back. See [Your tool's files](https://myeducator-llc.github.io/voshi-docs/building/files).
A **location** is one thing an instructor can put in a course. A one-activity tool has one location, called `home`, and the AI Builder creates it for you. Open the **Locations** tab to see what your tool offers, and to add more if your tool has several activities — one per graded thing, one per distinct thing a student can open. See [Locations](https://myeducator-llc.github.io/voshi-docs/building/locations).
Only an `assessment` location can report a grade. Check its **points**: that becomes the gradebook column's value when an instructor places it.
The **Sandbox** tab is the course your tool has been running in. Its **Links** tab lets you place your locations the way an instructor would, through the same picker they see. Launch each link as a student and as an instructor, then check the Sandbox's **Launches** and **Grades** tabs to see what Voshi recorded. See [Testing in the Sandbox](https://myeducator-llc.github.io/voshi-docs/building/sandbox).
The Sandbox runs your **draft** — whatever you built or uploaded last. Courses never run the draft. When the tool is ready, **publish** it: Voshi records the draft as version 1, and that is the version a course gets when an instructor adds your tool. Later publishes make version 2, 3, and so on; courses keep the version they adopted until their instructor chooses to upgrade. See [Versions](https://myeducator-llc.github.io/voshi-docs/building/versions).
Your tool starts in **draft** status, which means instructors cannot see it yet. Once it works in the Sandbox and you have published a version, **contact the MyEducator team to activate your tool**. After activation, instructors can add it to their courses.
## Where to next
What your code is, what Voshi does for it, and the rules it lives by.
Launch details, storage, files, and grades — everything your tool can ask Voshi for.
What is in the folder, what manifest.json does, and how to edit the code yourself.
Draft, published versions, and how courses adopt them.
---
# Concepts
> Locations, extids, and other key terms — the vocabulary the rest of these docs is written in.
Source: https://myeducator-llc.github.io/voshi-docs/concepts
A "thing" your tool provides that can be placed in a course.
Your own permanent identifier for a location.
An instructor putting one of your locations into their course.
## Location
A **location** is a "thing" that your tool provides.
**Every graded item in your tool should be one location**, and every non-graded thing you want a student to be able to open should be one too. A location's grades land in a single gradebook column, so if you want two graded activities, you define two locations.
Most tools offer several "things" that can be placed in a course. Locations are how you define them. For example:
- A quiz on architecture in the Persian Empire.
- A currency conversion tool your tool provides.
- A digital textbook titled *Introductory Law* that your tool publishes.
A tool that provides only one "thing" should define a single location. Hosted tools call that one location `home`.
### Internal and external identifiers
Every location has two IDs, and both arrive in every launch:
| ID | Assigned by | What it's for |
| --- | --- | --- |
| **extid** | **Your tool** | The primary way your tool identifies a location. A location is found by tool id + extid. |
| **location id** | Voshi | Voshi's internal identifier. These start with the letter `I` and are unique across the entire Voshi ecosystem — across tools, schools, and courses. |
Your tool should key its own data and its own routing on the **extid**. The internal ID is there for correlating with Voshi's dashboard and logs.
## Extid
Extids are the primary way tools identify locations — during instructor linking and during student launches.
An extid is how your tool declares that a resource exists and can be added to a course. **Each extid must represent a unique "thing" that has identity and can be linked within a course.**
Extids are created by **your tool**, not by Voshi and not by the LMS. That is deliberate: it means your tool controls what "location" means inside your product.
### Extids identify a resource everywhere
An extid should identify the same resource consistently across semesters, courses, and schools. If a quiz on Persian architecture has `extid = quiz1` in one course, that same quiz in a second course also has `extid = quiz1`.
**Defining your extids is one of the most important design decisions your tool makes.** Extids are permanent and **cannot be changed once they have been linked into a course.** When you design your extid strategy, think through:
- **Resource identity** — what counts as one distinct "thing" in your tool? What things are graded?
- **Cross-semester persistence** — if an instructor is teaching the same course again next semester, they will likely want the same locations (perhaps with minor changes). Will these ids still be valid then?
- **Provisioning** - what needs to be set up (database objects, etc.) when a new course starts? When a new semester starts?
### Rules for extids
- Extids may contain **letters, numbers, dashes, and underscores**. No other special characters.
- Extids should **not** normally include course-specific identifiers — no context id, semester code, or instructor id.
### When extids are used
1. **During placement**, when an instructor adds links to your tool in their course, Voshi shows them the locations your tool currently offers — each one an extid plus a label, a description, points, and so on. A hosted tool declares them in the dashboard or in its `manifest.json` (see [Locations](https://myeducator-llc.github.io/voshi-docs/building/locations)); a self-hosted tool answers a request from Voshi with the list (see [Serving your locations](https://myeducator-llc.github.io/voshi-docs/advanced/locations-endpoint)).
2. **Extids must be assigned during link placement, at course setup time.** They cannot be assigned later, during student launches.
3. **When a student clicks a link**, the launch names the linked extid. Your tool reads the extid and shows the matching resource, quiz, or tool. In a hosted tool it is `api.location.extid`.
4. **When your tool reports a grade**, the score goes to the gradebook column for that student at that location. The extid the instructor placed is what decides which column the score lands in.
### Examples of extid strategies
A music tool provides five digital instruments for students to interact with. The builder decides each instrument is a linkable resource, and defines five static extids offered to every course: `clarinet`, `piano`, `trumpet`, `violin`, `timpani`.
A test prep tool provides three practice exams. It defines three static extids offered to every course: `practice1`, `practice2`, `practice3`.
A discussion tool lets students post questions and answers during instructor lectures. Instructors are expected to place many links in a course — one per lecture day. Each link holds its own discussion and its own distinct data, so each link genuinely is its own "thing." Each time an instructor places a link, this tool generates a single `uuid4`-based extid as its available location. This tool therefore creates a large number of dynamic locations, one at a time as links are placed. Only a [self-hosted](https://myeducator-llc.github.io/voshi-docs/advanced/overview) tool can do this, because it needs a server to answer each placement with a fresh extid; a hosted tool offers a fixed list.
## Placement
A **placement** is an instructor putting one of your locations into their course — the link students click. Placements happen through a content picker that Voshi presents: the instructor sees the locations your tool currently offers, picks one, and confirms. For a graded location, that's also when the gradebook column is created.
Placement is where an extid gets attached. Everything downstream — the launches students produce, the storage rows, the gradebook column your grades land in — follows from the extid the instructor placed.
### One location per thing
The rule that keeps an integration simple:
> **Define 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. If it's a distinct thing a student can open, it's its own location. Nothing else needs to exist.
**Don't design around placing the same location more than once in a course.** A location is one "thing" in your tool, and it should correspond to one link in the course and — when it's graded — one gradebook column.
If you want two graded activities, define two locations with two extids. Reaching for two placements of the same location instead means both links point at the same resource, share the same [storage](https://myeducator-llc.github.io/voshi-docs/api/storage) rows, and compete for the same gradebook column. All grades for a location go to a single column regardless of how many links exist, so the extra placement gets you nothing and confuses the instructor's gradebook.
### Duplicate placements happen anyway
Even though your tool shouldn't rely on them, duplicate links to the same location do occur, and you can't prevent them: instructors duplicate links, LMS platforms roll courses forward into new terms in their own ways, and a link deleted from one module can be added back in another. Your tool is never notified when any of that happens.
Your tool doesn't need to do anything about it — the behavior is already consistent:
- All launches carry the same `location.extid`, so they all resolve to the same resource in your tool.
- All of them share the same location storage rows.
- **Grades always go to the first gradebook line item** for that extid, so a student's score for a location always lands in the same place.
This is exactly why Voshi identifies resources by location and extid. The individual links in the course — and the LTI `resource_link_id` behind them — are outside Voshi's control and shouldn't carry meaning in your tool. **Identify your resources by extid.**
## Next
Register a tool, build it, run it in the Sandbox, and publish.
Types, points, and how to declare the locations your tool offers.
---
# How a hosted tool works
> Your tool is a small piece of browser code. Voshi hosts it, launches it, and hands it everything it needs.
Source: https://myeducator-llc.github.io/voshi-docs/building/how-it-works
A hosted tool is a **web component**: one custom HTML element, defined in a JavaScript file, that Voshi loads inside the course page when someone opens your tool. Voshi does everything around it — the LMS integration, hosting your files on a CDN, logging the student in, placement in courses, provisioning, storage, and grade passback. Your code draws the activity and talks to Voshi through one object.
## The lifecycle of a launch
```mermaid
sequenceDiagram
autonumber
participant LMS
participant Voshi
participant Tool as Your element
LMS->>Voshi: Student clicks your link in the course
Voshi->>Tool: Load your files, then new YourTool(api)
Tool->>Voshi: api.storage.memberLocation.get()
Tool->>Tool: Render the activity
Tool->>Voshi: api.submitGrade({ score: 0.8 })
Voshi->>LMS: Score lands in the gradebook
```
1. **Someone opens your tool** from a course. Voshi handles the LMS side and works out who they are, which course they are in, and which of your [locations](https://myeducator-llc.github.io/voshi-docs/building/locations) the link points at.
2. **Voshi loads your code** — the version of your tool that course runs — and your styles and templates.
3. **Voshi constructs your element**, passing it one argument: the [Voshi API](https://myeducator-llc.github.io/voshi-docs/api/overview), already loaded with the launch details and ready to call.
4. **Your element renders** whatever the learner should see, reads any saved work from `api.storage`, and, when the learner finishes graded work, calls `api.submitGrade()`.
The page around your element belongs to Voshi. It watches your element's height and tells the LMS, so the frame fits your content — give your element `:host { display: block }` and it just works.
## What your code looks like
The smallest possible tool:
```javascript
// app.js
class HelloTool extends HTMLElement {
constructor(api) {
super()
this.api = api // keep the Voshi API for later
this.attachShadow({ mode: 'open' }) // your markup and styles live in here
}
connectedCallback() {
const name = this.api.isStaff ? this.api.user.full_name : 'there'
this.shadowRoot.innerHTML = `
Hello, ${name}. You opened ${this.api.location.label}.
`
}
}
customElements.define('hello-tool', HelloTool)
```
Three things every tool does, and the AI Builder does them for you:
- **The constructor takes `api` and stores it.** It attaches a shadow root and nothing else; browsers forbid a constructor from touching the page.
- **Rendering happens in `connectedCallback`**, after the element is on the page. Query `this.shadowRoot`, never `document` — your element's markup is inside the shadow root, and `document.querySelector` cannot see it. This is the most common mistake in hand-written tools.
- **The tag is registered with `customElements.define`.** The tag must be lowercase and contain a dash.
A real tool usually keeps its markup in `index.html` and its styles in `styles.css`, which Voshi loads for it and hands over as `api.assets`. See [Your tool's files](https://myeducator-llc.github.io/voshi-docs/building/files) and [Assets](https://myeducator-llc.github.io/voshi-docs/api/assets).
## What Voshi does for you
| Need | How it is met |
| --- | --- |
| Hosting | Your files are served from a CDN. Upload or build, and they are live in the Sandbox. |
| Login and identity | `api.user` says who launched, with a stable ID. No passwords, no accounts, no sessions to manage. |
| Which course, which activity | `api.course` and `api.location`. |
| Saving work | `api.storage` — four JSON rows per launch, plus files on each. See [Storage](https://myeducator-llc.github.io/voshi-docs/api/storage). |
| Grades | `api.submitGrade()`. See [Grades](https://myeducator-llc.github.io/voshi-docs/api/grades). |
| Course setup | Automatic. When a course is copied into a new term, its course and location storage come along. |
| Placement in courses | Instructors pick from your [locations](https://myeducator-llc.github.io/voshi-docs/building/locations) in a picker Voshi presents. |
| Versions | You publish; courses adopt a version and keep it. See [Versions](https://myeducator-llc.github.io/voshi-docs/building/versions). |
## The rules
Hosted tools run inside other people's course pages, so a few things are off limits. Voshi checks what it can at upload time, and the AI Builder already follows them.
- **No server, no network calls of your own.** Everything your tool needs from the platform comes through `api`; the only thing it ever fetches is its own files, through `api.assets`. There is no API key, because there is no server to keep one on.
- **No libraries, no CDNs, no build step.** Your files are plain JavaScript, CSS, and HTML, served exactly as uploaded. `import` other files of your own by relative path; a bare name like `import 'lit'` cannot be resolved.
- **No `localStorage` or `sessionStorage`.** Nothing about a learner may be kept in the browser. Use `api.storage`.
- **No `document` queries, no `eval`.** Work inside `this.shadowRoot`.
- **Keep state in the element** unless it must survive between visits, in which case it goes in `api.storage`.
## Where things happen
Everything about your tool lives in the [dashboard](https://myeducator-llc.github.io/voshi-docs/building/dashboard): building, testing, locations, versions, logs. There is nothing to configure anywhere else.
## Next
AI Builder, Sandbox, Locations, Launches, Grades, Pricing, Settings.
Everything on the `api` object.
---
# The dashboard
> Where you build, test, and manage your tool: a tour of each tab.
Source: https://myeducator-llc.github.io/voshi-docs/building/dashboard
Everything about a hosted tool happens in the [Voshi dashboard](https://zen.voshi.com/app/ltiaas/s/). Open **Tools**, pick your tool, and the tabs below are down the left side.
**The dashboard calls tools "tools".** These docs say *tool* for what you build and *tool* only where the API does; the two mean the same thing.
## AI Builder
Your tool's first page, and where it gets built. The page has two panels:
- **Discuss Design** on the left is a conversation. Describe what the learner should be doing; the AI asks what it needs to know and says when it has enough. The **build** button (the wand) hands the whole conversation to the builder, which writes the tool and uploads it as your **draft**. A correction is just another message, followed by another build. A message that led things astray can be set aside (dimmed and struck through, reversibly) so the next build ignores it. **New conversation** starts over against the same tool; past conversations are kept.
- **Running Tool** on the right launches your draft — a real launch, in your tool's [Sandbox](https://myeducator-llc.github.io/voshi-docs/building/sandbox) course. Choose which location to launch and whether to launch as a **student** or an **instructor**. Both roles share one course, so an instructor launch sees what the student launch just saved, exactly as in a real course. **Refresh** launches again.
The Running Tool header also holds the two code controls:
- **Download Code** gives you the draft as a zip. It includes an `AGENTS.md` that briefs a coding assistant (Claude Code, Cursor, and the like) on the rules of a hosted tool, so you can edit the code outside the dashboard and bring it back.
- **Upload Code** takes a folder from your computer and makes it the draft. Choose the folder that holds `manifest.json`, or that holds `app.js` if there is no manifest.
See [Your tool's files](https://myeducator-llc.github.io/voshi-docs/building/files) for what is in the folder.
## Sandbox
A private practice course that only you can see, with your tool already in it. Its tabs mirror what a course has:
- **Links** are placements of your tool in the Sandbox course. **New Link** opens the same picker an instructor uses, so you can place any of your locations and launch it as a student or an instructor. **Reset** throws away everything stored at a link, so the next launch starts as a first launch; **Remove** deletes the link.
- **Launches** lists every launch in the Sandbox.
- **Grades** is the Sandbox's gradebook: the scores your tool reported, by link and by persona.
The Sandbox always runs your **draft**. See [Testing in the Sandbox](https://myeducator-llc.github.io/voshi-docs/building/sandbox).
## Locations
The destinations your tool offers an instructor — each with a **name**, an **external ID** (your tool's own, permanent identifier for it), a **Voshi ID**, and a **type**. **Add Location** adds one; **Edit** changes its label and type; **Stop offering** hides it from the picker without breaking links already placed. A tool with one activity has one location, `home`. See [Locations](https://myeducator-llc.github.io/voshi-docs/building/locations).
## Launches
Every launch of your tool, from every course: when, which course, which user, which location, and whether it succeeded. Filter by course, user, or location to follow one student's path, or to see whether a particular placement is being used.
## Grades
Every score your tool reported, from every course, with the result the LMS gave. A score the LMS refused shows here with the reason. Open one to see the full record.
## Pricing
Whether a student pays to launch your tool, and how much, with different prices for different audiences. A tool with no prices is free. Pricing is described on the Pricing tab itself and on Voshi's website; it is not part of your code — a paying student is sent to checkout before your tool is launched, and your tool never knows the difference.
## Settings
The tool's identity: its **Tool ID**, its **platform status**, its **hosting** (which was fixed at registration), and the **name** and **description** instructors see.
| Platform status | Meaning |
| --- | --- |
| **Draft** | The initial status. Instructors cannot see the tool, and it launches only from the Sandbox. |
| **Active** | Live: instructors can add it to courses. |
| **Suspended** | Disabled by the platform team. |
Only the Voshi platform team changes the status. When your tool works in the Sandbox and you have [published](https://myeducator-llc.github.io/voshi-docs/building/versions) a version, contact the MyEducator team to activate it.
A hosted tool has **no API key** and no URLs to configure, because it has no server. Those fields exist only for a [self-hosted](https://myeducator-llc.github.io/voshi-docs/advanced/overview) tool.
## Team members
Tools are managed by their members, and membership is flat: every member has full rights over the tool. The dashboard has no members screen yet — ask the MyEducator team to add or remove teammates.
---
# Your tool's files
> What is in a hosted tool's folder, what manifest.json does, and how to edit the code outside the dashboard.
Source: https://myeducator-llc.github.io/voshi-docs/building/files
A hosted tool is a folder of plain browser files. The AI Builder writes the folder for you; **Download Code** hands it to you as a zip, and **Upload Code** takes it back. Nothing in it is compiled, bundled, or installed — what you upload is what runs.
## The folder
A typical tool:
```
my-tool/
├── manifest.json how to load the tool, and the locations it offers
├── app.js the entry: defines the custom element
├── styles.css the tool's styles
├── index.html the tool's markup
└── lib/
└── scoring.js another module, imported by app.js
```
| File | Role |
| --- | --- |
| **The entry** (`app.js`) | Required. An ES module that defines your element and calls `customElements.define`. It is the only file Voshi imports; everything else it loads is what the entry imports or the manifest names. |
| **Stylesheets** (`.css`) | Voshi loads them and adopts them into your element's shadow root before it renders, so your styles apply without you doing anything. See [Assets](https://myeducator-llc.github.io/voshi-docs/api/assets). |
| **Templates** (`.html`) | Markup fragments — no ``, ``, ``, `