Errors
All Voshi API errors share one JSON envelope:
{
"id": "Ie4rR0r1d",
"api": "api",
"time": "2026-07-27T18:10:00+00:00",
"error": "ValidationError",
"message": "score must be between 0 and 1",
"details": {}
}
idstringA unique ID for this error occurrence. Include it when contacting support — it lets the Voshi team find the corresponding server-side report.
errorstringThe error class, matching the status code below.
messagestringA human-readable description of what went wrong.
detailsobjectStructured extras, when the error has them.
The response format is content-negotiated on the Accept header: API clients sending Accept: application/json (or no preference) get the JSON envelope; a browser gets an HTML error page carrying the same report ID.
Status codes
| Status | Error | When |
|---|---|---|
401 | AuthenticationError | Missing, malformed, invalid, or expired credentials — a bad API key, a suspended app, or a stale api.token. |
403 | NotAuthorized | Authenticated, but not allowed — e.g. an app data token used on another user's row. |
404 | Http404 | The resource doesn't exist — or belongs to another app. Voshi deliberately doesn't distinguish these, so other apps' launches and grades can't be probed. |
422 | ValidationError | The request was understood but a value is invalid. The message says which — see the per-endpoint error tables. Also how an unprovisioned course or location row answers. |
500 | ApiException | Something failed on Voshi's side. Retry, and report the error id if it persists. |
Validation failures return 422, not 400. If your HTTP client only treats 400 as a validation error, widen the check.
Provisioning errors
Two 422s come from the provisioning flow:
| Message | Cause |
|---|---|
App … has not been provisioned for Context … | A GET/PUT/PATCH on a context or location storage row whose level isn't provisioned yet. |
App … must be provisioned before its locations are provisioned | A PUT to a provision.location URL while that course's provision.context is still unfinished. Call the course URL first. |
Errors that don't reach your code
Launch-time failures (draft app, broken callback URL, a course that isn't provisioned yet) happen before your endpoint is called: the user sees a Voshi page with a report ID, and your app never hears about the launch. See errors on Voshi's side.
Deep-link failures work the same way, in the other direction: if your locations endpoint times out, refuses the request, or returns something Voshi can't read, the instructor sees the error and your app is told nothing.
Grade sync failures are not HTTP errors
A grade submission whose LMS sync fails still returns 200 — the failure is reported in-band via sync_status: "failed" and sync_error, so you can retry by re-POSTing. See sync status and retries.