Use the Box Public API from services, CI jobs, hosted workers, and product automation code. For typed clients, start with the Box SDKs, Python SDK, or TypeScript/JavaScript SDK.
Recommended flow
- Create a Box API key in the dashboard.
- Create a box with
POST /boxes, or resume/fork an existing box when the user is continuing prior work.
- Poll
GET /boxes/{boxId} until the box is ready or idle.
- Queue work with
POST /boxes/{boxId}/prompt.
- Read
GET /boxes/{boxId}/events while work is running.
- Use
POST /boxes/{boxId}/desktop when the user or your support team needs live computer-use visibility.
- Stop/archive, resume, fork, or delete the box according to your retention policy.
Base URL
https://ascii.dev/api/box/v1
Authentication
Pass a Box API key as a bearer token on every request:
curl -sS "https://ascii.dev/api/box/v1/boxes" \
-H "Authorization: Bearer $BOX_API_KEY"
Create and rotate service keys in the Box dashboard or through the v1 API key endpoints. For key lifecycle guidance, see API Keys.
Treat Box API keys and returned desktop URLs as secrets. Desktop and VNC URLs can contain access tokens and should not be logged or persisted unredacted.
Response envelope
Every v1 JSON response has an explicit success discriminator:
{
"ok": true,
"type": "box.list",
"boxes": []
}
| Field | Description |
|---|
ok | true for success, false for failure. |
type | Stable response or event discriminator, such as box.list, box.created, box.stopping, or prompt.queued. |
Error model
HTTP status remains authoritative. Error bodies also use a structured JSON envelope:
{
"ok": false,
"type": "box.error",
"status": 409,
"code": "provider_not_configured",
"message": "Prompting is locked until Codex is configured on the Agents page.",
"error": {
"code": "provider_not_configured",
"message": "Prompting is locked until Codex is configured on the Agents page.",
"status": 409,
"details": {
"provider": "codex",
"setupUrl": "https://box.ascii.dev/dashboard?tab=agents"
}
},
"requestId": "req_..."
}
| Status | Typical codes | What to do |
|---|
400 | invalid_json, prompt_required, provider_required, invalid_name, no_changes, machine_not_running | Fix the request body, wait for the box machine to start, or retry once the box reaches a runnable state. |
401 | unauthorized | Provide a valid bearer token. |
402 | billing_required | Send the user to the returned billing URL. |
404 | not_found | Refresh local state; the box or key no longer exists. |
409 | account_not_ready, provider_not_configured, box_not_promptable, resume_failed, fork_failed | Resolve the prerequisite or resource-state conflict. |
429 | rate_limited, daily_limit_reached, limit_reached | Back off and show the limit message. |
5xx | invalid_json_response, stream_failed, http_500 | Retry idempotent calls with jitter and include requestId in support logs. |
Box lifecycle
A Box moves through these states:
| State | Meaning | Client behavior |
|---|
provisioning, provisioned, cloning | The sandbox is being created or prepared. | Poll GET /boxes/{boxId}. |
ready, idle | The box can accept prompts and interactive access. | Prompt, open desktop, SSH, or inspect events. |
running | The Box is actively working. | Read events; interrupt only when the caller intends to stop current work. |
archiving | Stop/snapshot is in progress. | Poll until archived or another terminal state. |
archived | The machine is stopped; a snapshot may be available. | Resume or fork. |
error | Provisioning or runtime failed. | Show the error and let the user stop, delete, resume, or fork when allowed. |
Providers, models, and reasoning
POST /boxes/{boxId}/prompt accepts the same providers Box uses in the dashboard. The API accepts codex and claude-code; claude is accepted as an alias for claude-code by the current backend, but claude-code is the canonical value to store in integrations.
Model ids come from the same catalog used by the dashboard model selector. If you omit model, Box uses the user’s saved dashboard default for that provider. Prefer the listed model ids for predictable behavior.
| Provider | Models | Reasoning effort values | Default |
|---|
codex | gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.3-codex | none, low, medium, high, xhigh (gpt-5.3-codex supports low, medium, high, xhigh) | gpt-5.4 |
claude-code | opus, sonnet, haiku | opus: low, medium, high, max; sonnet: low, medium, high; haiku: no reasoning control | sonnet |
Use the dashboard’s provider setup page to configure credentials before prompting. If credentials are missing, the API returns provider_not_configured with a setup URL.
Request examples
Set shared variables:
export BOX_API_BASE="https://ascii.dev/api/box/v1"
export BOX_API_KEY="box_your_secret_here"
Create a one-hour box and store its id:
BOX_ID=$(curl -sS -X POST "$BOX_API_BASE/boxes" \
-H "Authorization: Bearer $BOX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ttlSeconds":3600}' | jq -r '.box.id')
Create a box without automatic archival:
curl -sS -X POST "$BOX_API_BASE/boxes" \
-H "Authorization: Bearer $BOX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ttlSeconds":null}'
Poll readiness:
curl -sS "$BOX_API_BASE/boxes/$BOX_ID" \
-H "Authorization: Bearer $BOX_API_KEY"
Prompt a Box to work in a repo:
curl -sS -X POST "$BOX_API_BASE/boxes/$BOX_ID/prompt" \
-H "Authorization: Bearer $BOX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "codex",
"model": "gpt-5.4",
"reasoningEffort": "medium",
"prompt": "Work on the selected repo: run tests, fix failures, commit the result, and report the preview URL if you start a server."
}'
Read Box work and lifecycle events:
curl -sS "$BOX_API_BASE/boxes/$BOX_ID/events" \
-H "Authorization: Bearer $BOX_API_KEY"
Get a desktop streaming URL for live inspection:
curl -sS -X POST "$BOX_API_BASE/boxes/$BOX_ID/desktop?vnc=1" \
-H "Authorization: Bearer $BOX_API_KEY"
To return a VNC URL that does not require an access token, send:
curl -sS -X POST "$BOX_API_BASE/boxes/$BOX_ID/desktop?vnc=1" \
-H "Authorization: Bearer $BOX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"publicAccess":true}'
Stop/archive a box when the workflow is complete:
curl -sS -X POST "$BOX_API_BASE/boxes/$BOX_ID/stop" \
-H "Authorization: Bearer $BOX_API_KEY"
Endpoint reference
| Endpoint | Reference |
|---|
GET /me | Get current Box user |
GET /limits | Get Box limits |
GET /repos | List GitHub repositories available to Box |
POST /repos | Select repository for Boxes |
GET /api-keys | List API keys |
POST /api-keys | Create API key |
DELETE /api-keys/{apiKeyId} | Revoke API key |
POST /api-keys/{apiKeyId}/rotate | Rotate API key |
GET /secrets | Get Box secrets setup |
POST /secrets | Update Box secrets setup |
GET /boxes | List boxes |
POST /boxes | Create box |
GET /boxes/{boxId} | Get box |
PATCH /boxes/{boxId} | Update box |
DELETE /boxes/{boxId} | Delete box |
POST /boxes/{boxId}/stop | Stop and archive box |
POST /boxes/{boxId}/resume | Resume box |
POST /boxes/{boxId}/fork | Fork box |
POST /boxes/{boxId}/prompt | Prompt Box |
GET /boxes/{boxId}/events | List box events |
POST /boxes/{boxId}/interrupt | Interrupt running work |
POST /boxes/{boxId}/desktop | Get desktop streaming URL |
POST /boxes/{boxId}/sshkey | Configure box SSH key |