Skip to content

For developers · API docs

Canvas sandboxes API

A private Canvas LMS on its own virtual machine, on demand, to build and test LTI 1.3 tools and Canvas API integrations. This REST API creates, starts, stops and connects to sandboxes from a script, a CI job or a coding agent. The same operations are also available as MCP tools (see the MCP section).

Quick start

  1. Sign in to the developer dashboard with Google, add credit, and make an API key.
  2. Create a sandbox with POST /v1/sandboxes. The call answers at once; Canvas takes a couple of minutes to come up.
  3. Poll GET /v1/sandboxes/:id until state is ready.
  4. Read the URL, the admin login, the Canvas API token and the LTI values from GET /v1/sandboxes/:id/connection.
  5. Stop the sandbox when you are done. Stopping keeps the disk and all Canvas data.

Base URL and authentication

Base URL: https://sandbox-api.lmsystems.ai

Send your API key as a bearer token:

Authorization: Bearer lms_sk_…
  • Make and revoke API keys, and buy credit, on the dashboard. A key cannot make keys or buy credit: those routes answer 403 to a key.
  • Keep the key on a server or in a script. Browsers may call the API only from the dashboard's own origin.
  • Bodies and responses are JSON. Send Content-Type: application/json with a body. A body with a field the route does not know is refused with 400.
  • Limits: 300 requests a minute per key, and 20 sandbox creations a minute. Past a limit the API answers 429.

Errors

An error is JSON with a message, and sometimes more fields:

{ "error": "Sandbox not found" }
StatusMeaning
400The body, a query value or the Idempotency-Key header is not valid. The message names the field.
401No API key, or a key that is wrong or revoked.
402Not enough credit to start. The body also has code: "insufficient-credit", balanceUsd and neededUsd.
403The account is frozen (after a payment dispute), or the route needs a dashboard sign-in.
404No such sandbox. Another account's sandbox looks the same.
409The request conflicts with the sandbox or the account: the account is at its sandbox limit, the sandbox is being deleted, or Canvas has not finished its first boot.
429Too many requests.
503There is no sandbox capacity right now. Try again later.
5xx{ "error": "Unexpected server error" }

The sandbox object

Every sandbox route answers with a sandbox, or a list of them:

{
  "sandbox": {
    "id": "sbx-abc123def4",
    "name": "lti-test",
    "size": "standard",
    "state": "provisioning",
    "desired": "running",
    "url": "https://sbx-abc123def4.canvas.lmsystems.app",
    "idleStopMinutes": 60,
    "createdAt": "2026-10-30T15:00:00.000Z",
    "startRequestedAt": "2026-10-30T15:00:00.000Z",
    "price": { "runningPerHourUsd": 0.27, "diskPerMonthUsd": 20 }
  }
}
FieldMeaning
idsbx- and 10 letters or digits.
nameYour label for it.
sizestandard or large.
stateWhat the VM is doing now. See the states below.
desiredWhat you asked for: running, stopped or deleted.
stopReasonOnly when stopped: user, idle (no sign-in traffic for idleStopMinutes), credit (balance reached $0) or frozen.
urlThe sandbox's Canvas address.
idleStopMinutesMinutes without signed-in traffic before it stops itself.
createdAt, startRequestedAt, readyAt, lastActivityAtISO 8601 times. readyAt is when Canvas last answered; the others are set once they happen.
deleteAtOnly when being deleted: when its data goes.
lastErrorA message, when the last VM operation failed.
priceWhat this sandbox costs: runningPerHourUsd while running, diskPerMonthUsd for its disk.

States:

stateMeaning
provisioningA new sandbox, on its first boot.
startingBooting from its disk.
readyCanvas answers. The only state in which you can use it.
stoppingShutting down.
stoppedOff. The disk and all Canvas data are kept.
resettingBeing replaced by a fresh Canvas.
deletingStopped, and its data goes at deleteAt. It can be restored until then.

Start, stop, reset, restore and delete only record what you want and answer 202. A background process then changes the VM. Wait on state, not on elapsed time.

Endpoints

List sandboxes

GET /v1/sandboxes answers { "sandboxes": [ … ] }, newest first.

Create a sandbox

POST /v1/sandboxes creates a sandbox and starts it. It answers 202 with the sandbox in state provisioning.

Body fieldMeaning
nameOptional. 1–60 characters. Defaults to the id.
sizeOptional. standard (default) or large.
idleStopMinutesOptional. 10–1440, default 60.

Add an Idempotency-Key header (up to 200 letters, digits and . _ : -) to make a retry safe: a repeated key returns the sandbox the first call made instead of making another.

curl -X POST https://sandbox-api.lmsystems.ai/v1/sandboxes \
  -H "Authorization: Bearer $SANDBOX_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: lti-test-1" \
  -d '{"name":"lti-test"}'

It answers 402 when the balance is below an hour of the sandbox running, 409 when the account is at its sandbox limit (a sandbox being deleted does not count), and 503 when there is no capacity.

Get a sandbox

GET /v1/sandboxes/:id answers { "sandbox": { … } }.

Update a sandbox

PATCH /v1/sandboxes/:id with name and/or idleStopMinutes (at least one) answers { "sandbox": { … } }.

Start a sandbox

POST /v1/sandboxes/:id/start runs a stopped sandbox. Its Canvas and data are as you left them. It needs credit for at least an hour of running (402 if not) and answers 409 if the sandbox is being deleted.

Stop a sandbox

POST /v1/sandboxes/:id/stop stops it. Running time stops being billed. The disk and all Canvas data are kept, and the disk is still billed until the sandbox is deleted.

Reset a sandbox

POST /v1/sandboxes/:id/reset replaces the sandbox's Canvas with a fresh one, with a new admin password and API token. Everything in the old Canvas is lost. It needs the same credit as a start.

Delete and restore a sandbox

  • DELETE /v1/sandboxes/:id stops the sandbox and deletes it with all its data 24 hours later (deleteAt).
  • DELETE /v1/sandboxes/:id?immediately=true deletes it at once. This cannot be undone.
  • POST /v1/sandboxes/:id/restore cancels a delete within the 24 hours. The sandbox comes back stopped. It answers 409 if the sandbox is not being deleted, or if its disk is already going.

Connection details

GET /v1/sandboxes/:id/connection has everything you need to use the sandbox's Canvas. It is never cached.

{
  "id": "sbx-abc123def4",
  "state": "ready",
  "url": "https://sbx-abc123def4.canvas.lmsystems.app",
  "loginUrl": "https://sbx-abc123def4.canvas.lmsystems.app/__lmsystems/enter?t=…",
  "loginUrlExpiresAt": "2026-10-30T15:13:00.000Z",
  "canvas": {
    "adminEmail": "admin@canvas.local",
    "adminPassword": "…",
    "apiToken": "…",
    "apiBase": "https://sbx-abc123def4.canvas.lmsystems.app/api/v1"
  },
  "lti": {
    "issuer": "https://canvas.instructure.com",
    "authorizationEndpoint": "https://sbx-abc123def4.canvas.lmsystems.app/api/lti/authorize_redirect",
    "tokenEndpoint": "https://sbx-abc123def4.canvas.lmsystems.app/login/oauth2/token",
    "jwksUrl": "https://sbx-abc123def4.canvas.lmsystems.app/api/lti/security/jwks"
  }
}
  • loginUrl opens Canvas in a browser, signed in as the admin. It works for 10 minutes (loginUrlExpiresAt).
  • canvas.apiToken is an admin token for Canvas's own REST API at canvas.apiBase. Send it as Authorization: Bearer <token>.
  • lti has the LTI 1.3 platform values to register your tool with.
  • note appears when the sandbox is not ready: the values are from its last boot, and the sandbox must be started to use them.
  • It answers 409 while Canvas has not finished its first boot (the body has state), and when the sandbox is being deleted.
  • The admin password and API token are secrets. Do not log, print or commit them.

Account

  • GET /v1/balance answers { "balanceUsd": 12.5, "credits": [{ "amountUsd", "kind", "at" }] }: the balance and the last 20 credit entries.
  • GET /v1/usage?month=YYYY-MM answers { "month", "sandboxes": [{ "sandboxId", "runningHours", "diskGbHours", "chargedUsd" }] }. The month defaults to the current one.
  • GET /v1/pricing needs no key. It answers { "sizes": [{ "id", "vcpus", "memoryGb", "runningPerHourUsd" }], "disk": { "gb", "perMonthUsd" }, "billing" }.

Example: create a sandbox and use its Canvas

export SANDBOX_KEY=lms_sk_…        # from the dashboard
BASE=https://sandbox-api.lmsystems.ai
AUTH="Authorization: Bearer $SANDBOX_KEY"

# 1. Create it
id=$(curl -s -X POST "$BASE/v1/sandboxes" -H "$AUTH" -H "Content-Type: application/json" \
  -H "Idempotency-Key: lti-test-1" -d '{"name":"lti-test"}' | jq -r .sandbox.id)

# 2. Wait until Canvas answers
until [ "$(curl -s "$BASE/v1/sandboxes/$id" -H "$AUTH" | jq -r .sandbox.state)" = ready ]; do sleep 10; done

# 3. Get the connection details (they hold secrets)
curl -s "$BASE/v1/sandboxes/$id/connection" -H "$AUTH" > connection.json

# 4. Call Canvas's own API with the admin token
curl -s "$(jq -r .canvas.apiBase connection.json)/courses" \
  -H "Authorization: Bearer $(jq -r .canvas.apiToken connection.json)"

# 5. Stop it when you are done
curl -s -X POST "$BASE/v1/sandboxes/$id/stop" -H "$AUTH"

Timing, idle stop and billing

  • A new or reset sandbox usually reaches ready in about 2 minutes. Allow up to 7. A start from stopped usually takes about 2 minutes. Allow up to 5. Poll every 5–10 seconds.
  • A sandbox stops itself after idleStopMinutes (10–1440, default 60) without signed-in traffic: a browser session, or a request that carries an Authorization header. It stops with stopReason: "idle", and its data is kept.
  • It also stops when the balance reaches $0 (credit) and when the account is frozen (frozen).
  • Running time is billed per second. The disk is billed from creation until the sandbox is deleted, including while it is stopped, so a balance can go below $0. GET /v1/pricing has the prices.
  • Create, start and reset need credit for at least one hour of running. Credit is prepaid and is bought on the dashboard.

MCP

The same operations are MCP tools at https://sandbox-api.lmsystems.ai/mcp (HTTP transport). Use an API key; the MCP endpoint takes no dashboard sign-in. The tools are list_sandboxes, create_sandbox, get_sandbox, wait_for_sandbox, start_sandbox, stop_sandbox, reset_sandbox, delete_sandbox, restore_sandbox, get_connection and get_account. To add the server to Claude Code:

claude mcp add --transport http canvas-sandbox https://sandbox-api.lmsystems.ai/mcp --header "Authorization: Bearer $LMSYSTEMS_API_KEY"

Notes for AI agents

  • Ask the user before you reset or delete a sandbox, or send immediately=true. They destroy data.
  • Never print, log or commit the admin password or the Canvas API token. Read them from connection into a variable or a file that is not committed.
  • Wait on state === "ready", and check lastError if a sandbox stays in one state for longer than the times above.
  • Stop a sandbox you are done with. A running sandbox is billed until it idles out.