---
title: "Create a sandbox. Every field is optional."
method: POST
path: "/v1/sandboxes"
tags: ["sandboxes"]
---

# Create a sandbox. Every field is optional.

`POST /v1/sandboxes`

## Request

- Base URL: `https://api.withruntime.com`
- URL: `https://api.withruntime.com/v1/sandboxes`
- Auth: HTTP bearer

A Linux microVM with its own kernel and disk. With no body you get the free usage while the month has some left, the default region and a 2 vCPU / 4096 MiB / 4096 MiB machine that starts from a warm template. When no server has room it answers 409 no_capacity with Retry-After and charges nothing: retry with the same Idempotency-Key, which runs the create again (a refusal is never replayed). Send `Prefer: wait=N` (seconds, at most 120) to get the answer once the sandbox has settled; the SDKs do.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `name` string: Your own handle for the resource. Letters, digits, dot, dash, underscore.
  - `labels` object: Your own key/value tags, at most 32. Filter lists with label=key:value.
  - `env` object: Environment variables for every command, process, terminal, SSH session and the image's start command in this sandbox, under each command's own env. At most 32 variables and 32768 bytes of names and values. Values are never shown again: answers list only envNames.
  - `secrets` string[]: The egress secrets this sandbox may use, by name, at most 64: it gets only those of your egress secrets (POST /v1/secrets) that would otherwise reach it, so a secret's own `only` still applies and is never widened. [] is none. Omit it for every secret that reaches it. A name with no secret yet is kept, and takes effect once you create that secret. Fixed for the sandbox's life; a fork or a copy of a snapshot keeps its source's list and may only narrow it. A sandbox outside a secret's list sees no placeholder for it, and its requests never carry the value.
  - `funding` 'trial' | 'paid': Accepted and not read: the account decides. An account with prepaid credit (or a purchase it kept) runs paid sandboxes, which use the free usage first and then credit, with nothing to change; an account without credit runs sandboxes within the limits without credit.
  - `region` string: Omit for the default region.
  - `vcpu` integer: At most 16 (2 without credit).
  - `memoryMiB` integer: At most 65536, 64 GiB (4096 without credit).
  - `diskMiB` integer: At least 3072; at most 409600, 400 GiB (10240 without credit).
  - `cpu` 'shared' | 'reserved'
  - `cpuFloorMillis` integer: Guaranteed CPU in thousandths of a vCPU while shared. Reserved CPU guarantees all of it.
  - `timeoutSeconds` integer: A time limit you choose, 60 to 86400 seconds (24 hours): the sandbox pauses (or stops, with onTimeout) when it runs out, busy or not, unless extended. Default 0, no limit: it runs while it works and pauses when idle (idlePauseSeconds), until you stop it or credit runs out. Without credit, the limit is capped at the free usage left.
  - `pausable` boolean: Ignored since 5 October 2026: every sandbox can pause.
  - `idlePauseSeconds` integer: Pause the sandbox after this many seconds with no request, no command or terminal still running, no open preview, port, SSH or tunnel connection, no network traffic and no CPU use by its processes. Nothing is lost, and with autoWake the next request wakes it. Default 60 unless persistent; 0 is never; otherwise 10 to 86400.
  - `autoWake` boolean: Wake the sandbox by itself when it is paused and a request needs it: an exec, a file, process, terminal or desktop call, or a visit to a shared port. It is billed as a wake from the moment it wakes. Default true.
  - `persistent` boolean: Keep it running until you stop it, while credit lasts: no time limit even with timeoutSeconds, and no idle pause unless idlePauseSeconds asks for one. Its disk is billed as any sandbox's. Paid only.
  - `maxTotalCostMicros` integer: The most this sandbox may cost over its whole life, in microdollars; it stops there.
  - `getOrCreate` boolean: With name: answer the sandbox that already has this name instead of refusing name_taken, waking it if paused and restarting it from its disk if stopped. The other fields apply only when it is created.
  - `onTimeout` 'pause' | 'stop' | 'delete': What happens when its timeoutSeconds, its credit or a spending limit ends it: pause (keep memory and files; wake later), stop (keep the files, billed as paused storage; restart later) or delete (when timeoutSeconds runs out, delete it and its files; credit or a spending limit only stops it). Default pause. onLeaseEnd, its older name, is accepted too.
  - `onLeaseEnd` 'pause' | 'stop' | 'delete': The older name of onTimeout, still accepted. Send one, or the same value in both.
  - `maxCostMicros` integer: Deprecated, still accepted from older clients: use maxTotalCostMicros.
  - `network` object: Network rules from the first start, the same shape as PUT /v1/sandboxes/{id}/network. Omit for the default: the public web on ports 80 and 443.
    - `internet` boolean, required: false refuses every outbound connection.
    - `allow` string[]: When not empty, only these destinations are reachable, on every port the sandbox may use.
    - `deny` string[]: Never reachable. Wins over everything.
    - `connect` string[]: host:port pairs beyond ports 80 and 443. A paid sandbox of an organization with a paid purchase reaches every port already; list pairs here when you narrow it with allow. Sandboxes without credit stay on ports 80 and 443.
  - `image` string: Start from this ready image: its id, name (its latest tag), name:tag or name@version (POST /v1/images, runtime images build). The sandbox runs on the host that keeps it. When the image has a start command, create answers once its ready check passes, and says how in start.
  - `snapshot` string: Start as a copy of this ready snapshot (POST /v1/sandboxes/{id}:snapshot), on a server that holds it: the one it was taken on, or one holding a checked copy of it. A memory snapshot's copy continues its files, memory and running processes, and is the snapshot's size: another size is refused. A disk snapshot's copy boots afresh with its files, at the vcpu, memoryMiB and diskMiB you ask (the snapshot's for what you leave out), with a disk at least the saved one. Not with image.
  - `tailscale` object: Join your Tailscale network once running; paid sandboxes only.
    - `authKeySecret` string, required: The name of a job secret holding a Tailscale auth key (runtime secrets set NAME --jobs). Use an ephemeral, tagged key; a reusable one lets the sandbox log in again by itself after a long pause.
    - `hostname` string: The machine's name on the tailnet, a DNS label. Default: the sandbox's name, or runtime- and its id's first eight characters.
    - `tags` string[]: Tailscale tags to advertise; the key must be allowed them.
  - `volumes` object[]: Volumes to attach at start (POST /v1/volumes), on the same host as the sandbox.
    - `volumeId` string, required: Resource identifier (UUID).
    - `path` string, required: Where it is mounted, such as /data. Not under /etc, /usr, /proc and the like.
    - `mode` 'rw' | 'snapshot': rw: read-write, one sandbox at a time. snapshot: a read-only copy as it is now, for any number of sandboxes.
  - `spreadFrom` string[]: Ids of your sandboxes or snapshots, 1 to 16: this one starts on a server none of them is on, and stays apart if its start is moved. Use it to keep warm sources on separate servers, so copies of their snapshots start on several. Compare placement to check. When no other server has room, the create answers no_capacity and nothing is made. A copy of a snapshot can start only where that snapshot is kept.

## Response `200`

Success

- object
  - `id` string, required: Resource identifier (UUID).
  - `kind` 'sandbox', required
  - `name` string, nullable
  - `labels` object: Your own key/value tags, at most 32. Filter lists with label=key:value.
  - `status` 'pending' | 'active' | 'paused' | 'stopping' | 'stopped', required: Deprecated: read state. An older, coarser name for it, kept for older clients.
  - `state` 'starting' | 'running' | 'pausing' | 'paused' | 'resuming' | 'stopping' | 'stopped', required
  - `region` string, required
  - `funding` 'trial' | 'paid', required
  - `vcpu` integer
  - `memoryMiB` integer
  - `diskMiB` integer
  - `cpu` 'shared' | 'reserved'
  - `cpuFloorMillis` integer
  - `pausable` boolean
  - `timeoutSeconds` integer: Its time limit in seconds, up to 86400 (24 hours); 0 is none: it runs while it works and pauses when idle.
  - `onTimeout` 'pause' | 'stop' | 'delete': What happens when its time limit, credit or a spending limit ends it. The same value as onLeaseEnd, its older name.
  - `idlePauseSeconds` integer: Pauses after this many idle seconds; 0 is never.
  - `idlePauseUnusedOnly` boolean: True only for a sandbox created before 27 September 2026 with the old default idle pause, which paused it only while nothing had used it. False when the idle time counts from the last activity.
  - `autoWake` boolean: A request wakes it when paused: an exec, a file or terminal call, or a visit to a shared port.
  - `persistent` boolean: It has no time limit and is not paused for being idle unless idlePauseSeconds says so.
  - `diskKept` boolean: Stopped with its disk kept: :restart starts it again from the disk. A stop keeps the disk; only a delete removes it.
  - `lastActiveAt` string, date-time, nullable: The last exec, file, terminal, desktop or preview request, or open preview connection, to within five seconds (a minute for a sandbox whose idle time counts only while unused).
  - `createdAt` string, date-time, required
  - `readyAt` string, date-time, nullable: When its current run became ready to run commands. Null before that, while it is paused, and once a stop that kept its disk has ended the run; endedAt says when it ended.
  - `expiresAt` string, date-time, required: Paid up to, at most an hour ahead, or when a stopped sandbox ended: kept for older clients. Read endsAt for when it stops or pauses by itself.
  - `endsAt` string, date-time, nullable: When it stops or pauses by itself: the end of its time limit, which :extend moves, or where its funding ends when credit or a spending limit stops its renewal. null when it never will: no time limit, or not running.
  - `endedAt` string, date-time, nullable
  - `stopReason` string, nullable: Why it stopped or paused. Yours: requested, time_limit (its timeoutSeconds ran out and it was made to stop rather than pause; lease_expired before 4 October 2026), idle, paused_expired (kept paused past its retention), insufficient_funds (credit ran out), pilot_hours_used (its pilot's sandbox hours are used; credit does not run it, and the account's owner may write to support to extend the pilot or ask for more hours), pilot_ended (its pilot and the pilot's grace are over; credit does not run it, and the account's owner may write to support to extend the pilot), lifetime_cap (this sandbox's own maxTotalCostMicros), spending_limit (an owner-set spending limit, usually this agent's daily limit; see what is left with GET /v1/limits or runtime limits) or authority_revoked (the key that made it lost the right). A fault on our side, not in your code: host_lost (its machine failed; create a new sandbox) and pause_failed (its memory could not be kept; it is paused with its files, and the next wake starts it from its disk, memoryRestored false). operator_stopped: our operator stopped it. startup_failed: it stopped before it was ready to run commands; from your own image, check its start and ready commands. account_busy_here: it can start only on the servers that hold its snapshot or image, and your own sandboxes there were short of CPU, so it did not start and nothing ran; start it again in a minute. service_unready: a service on our side that it needs was unavailable, so it could not keep running; create a new one. host_stopped: its machine ended without a stop being asked for; while it was starting or waking that is our fault, and once it was running it can also be a shutdown or crash inside the sandbox.
  - `pausedAt` string, nullable
  - `pausedExpiresAt` string, nullable: When a paused or stopped sandbox is deleted. Null while it is kept as long as your account has credit.
  - `memoryRestored` boolean
  - `placement` string, nullable: Opaque tag of the server it is on: the same for your sandboxes and snapshots on one server, different on another, never the server's name. Compare tags to see whether two share a server; keep them apart with spreadFrom on create. Null before it is placed.
  - `chargedMicros` integer: Settled, in microdollars (1,000,000 = $1).
  - `heldMicros` integer: Deprecated, kept for older clients: read chargedMicros and GET /v1/usage.
  - `rates` object
  - `envNames` string[]: The names of its own environment variables (env), sorted, on a create, an update and a read of one sandbox. Values are never shown.
  - `secrets` string[], nullable: The egress secrets it may use, fixed when it was created: only those of your secrets that would otherwise reach it. null: every secret that reaches it. On a create, an update and a read of one sandbox.
  - `secretsEnforced` boolean: On an update with labels by a key that may read secrets, when one of your egress secrets is scoped: true when every server running your sandboxes holds the change, so the next request from this sandbox is rewritten (or no longer is) by the new labels. False when a server has not confirmed yet; it takes the change within 15 seconds. Absent for any other key: its change reaches the servers within 15 seconds.
  - `simulated` boolean
  - `replayed` boolean: Present and true when an Idempotency-Key replay answered.

## Other responses

- `default`: Error

---

[API](https://skmtc.dev/withruntime/apis/runtime-cloud-api.md) · [All operations](https://skmtc.dev/withruntime/apis/runtime-cloud-api/llms.txt) · [OpenAPI document](https://skmtc.dev/withruntime/apis/runtime-cloud-api/revisions/6f4012286988?raw)
