Management dashboard JSON API¶
The /api/v1 API served by rayito dashboard
and consumed by its page. It is the same for all the dashboard's shells: the
local server today and, in the next phase of the dashboard OpenSpec change,
the stack in your account. All of them return documents with the same shape,
which are validated against the JSON schema versioned in the repository
(testdata/dashboard/api-v1.schema.json),
and the same contract tests run against each shell.
It is not a public service API
There is no Rayito server: the API is served by a process of yours, on
127.0.0.1, while the dashboard runs. It exists for the page and for
testing it; write your scripts with the SDK services
(Quotas, UsageCost and Fleet).
Access (local shell)¶
All routes require the session cookie that the server hands out when the
bootstrap token is redeemed (Security);
POST requests additionally require Content-Type: application/json and the
X-Rayito-CSRF header with the token from GET /api/v1/session, a route that
only the local shell has. GET /api/v1/cost with billed=true also requires
that header, because each query is billed. A request with Sec-Fetch-Site
other than same-origin or none receives 403.
Versioning¶
The version is in the path (/api/v1). Within v1 only fields and routes
are added; removing or changing one requires /api/v2. Keys are in
camelCase, dates in ISO 8601 UTC and amounts in USD.
Reads¶
| Route | Parameters | Response |
|---|---|---|
GET /api/v1/meta |
API and Rayito version, region, allowActions, costExplorer (enabled, requestPriceUsd and cacheTtlSeconds), eventsConfigured, logsAvailable and the intervals and caps the page uses |
|
GET /api/v1/sandboxes |
state (repeatable), image |
sandboxes[] with sandboxId, state, image, imageArn, imageVersion, startedAt, memoryMib |
GET /api/v1/images |
filter |
images[] |
GET /api/v1/images/versions |
image (required) |
versions[], newest first |
GET /api/v1/quotas |
entries[] (quotas with their increaseUrl), memory (allocated, quota, fraction and level), tps[], warnings[] and thresholds |
|
GET /api/v1/cost |
billed=true, start, end, region, refresh |
estimated always and billed only with billed=true |
GET /api/v1/events |
sandboxId, type (repeatable), limit, order |
configured and events[] |
GET /api/v1/accounts |
accounts[] with the name of each additional account of the multi-account view (rayito dashboard --account); empty without it. With ?account=NAME on any other read route, that account is requested (always read-only) |
|
GET /api/v1/logs |
sandboxId (required), limit |
the last limit lines in chronological order, lines[] (timestamp, message, stream), truncated if there are earlier ones and fallback if the expected stream did not exist and the lines come from streams found by name (they may not be the sandbox's) |
Cost comes in two documents that are never mixed: estimated (with
label: "estimated", the dashboard's calculation) and billed (with
label: "billed", from Cost Explorer, with partial if AWS is still
estimating some period). billed=true without --cost-explorer is
403 cost_explorer_disabled and makes no paid query.
{
"sandboxes": [
{
"sandboxId": "microvm-00000000-0000-0000-0000-000000000001",
"state": "RUNNING",
"image": "rayito-base-2gb",
"imageArn": "arn:aws:lambda:us-east-1:123456789012:microvm-image:rayito-base-2gb",
"imageVersion": "1.0",
"startedAt": "2026-10-13T12:00:00Z",
"memoryMib": 2048
}
]
}
No response carries envs, runHookPayload, tokens or JWEs: they do not
exist in the domain model.
Actions¶
Only with --allow-actions; without it, all of them are 403 actions_disabled
and do not touch AWS. All require "confirm": true in the body and return
202 with the queued job (job).
| Route | Body | What it does |
|---|---|---|
POST /api/v1/sandboxes/{id}/{action} |
{"confirm": true} |
action is kill, suspend or resume on one sandbox |
POST /api/v1/actions |
{"action": "suspend", "sandboxIds": ["..."], "confirm": true} |
the same action on several (up to bulkMaxSandboxes from /meta) |
GET /api/v1/actions |
the latest jobs, most recent first | |
GET /api/v1/actions/{jobId} |
one job: status (queued, running, finished), completed, total, counts and items[] |
|
GET /api/v1/actions/plan |
action, count |
how long it would take: plan.tps and plan.estimatedSeconds |
A single worker runs the jobs one at a time, at the pace of the operation's
TPS quota (the applied one, if lower than the documented one). Each
items[] entry carries outcome (done, unchanged or failed) and, if it
failed, the AWS code in code; never the message.
Errors¶
Every error is {"error": {"code", "message"}} and, if it comes from AWS,
awsCode. The message is our own and never repeats AWS's. The codes are a
closed list:
code |
HTTP | When |
|---|---|---|
invalid_argument, invalid_json, confirmation_required |
400 | invalid parameter or body; confirm missing |
unauthorized |
401 | no session or bootstrap token already spent |
forbidden, csrf_failed, actions_disabled, cost_explorer_disabled, aws_access_denied |
403 | foreign Origin, CSRF, action or cost not enabled, or AWS rejected the credentials |
not_found, method_not_allowed |
404, 405 | unknown route or job; method not allowed |
payload_too_large, unsupported_media_type |
413, 415 | body larger than the cap; not JSON |
misdirected_request |
421 | foreign Host |
aws_throttled, jobs_busy |
429 | AWS throttled; the dashboard's queue already has the maximum number of unfinished actions |
aws_error, internal_error |
502, 500 | AWS returned another error; unexpected dashboard failure |