Sandboxes
Create isolated sandboxes through the OpenRelay API, run commands, transfer files, expose HTTP ports, and stop a sandbox when your work is done.
A sandbox is an isolated virtual machine for running commands, working with files, and serving applications. Start one from a template, reuse it across commands, and stop it when the work is done.
Sandboxes must be enabled for your organization. Otherwise these endpoints
return 404. File access, exposed ports, interactive terminals, pause and
resume, and group creation also require access to those features. Contact
support to enable access.
Authenticate and choose a template
Use an API key from your organization. Each endpoint's reference page lists its required permission.
export OPENRELAY_API_KEY="or_your_api_key"
curl https://api.openrelay.inc/v1/sandbox-templates \
-H "Authorization: Bearer $OPENRELAY_API_KEY"The response's data array contains built-in templates and your organization's
templates. Choose a template with state: "ready" and pass its id as
template. The examples below use python; check that your template list
includes it first. See List sandbox templates.
Create a sandbox and run a command
Send an Idempotency-Key so a retry with the same body returns the same sandbox.
Keep that key for retries, and generate a new one for each new sandbox.
CREATE_KEY="$(uuidgen)"
curl https://api.openrelay.inc/v1/sandboxes \
-H "Authorization: Bearer $OPENRELAY_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Idempotency-Key: $CREATE_KEY" \
-d '{
"template": "python",
"timeoutSeconds": 300,
"run": {"cmd": "python -c \"print(6 * 7)\""}
}'With run set and Accept: application/json, the request waits for the command
to finish. The response includes the sandbox's id, state, expiresAt, and
a result with exitCode, stdout, and stderr. The command finishing does
not stop the sandbox. Save its id for the next operations:
export SANDBOX_ID="the_id_from_the_create_response"Omit run to create a sandbox without a first command. Omit cpu and
memoryMiB to use the template's resources. A request for more CPU or memory
than the template's machine returns 400 RESOURCES_EXCEED_TEMPLATE.
For output as it arrives, send Accept: application/x-ndjson and use curl -N.
Each line is a JSON event: the first names the sandbox, followed by command
output and an exit event. Collected JSON output is limited to 1 MiB per stream;
use streaming for larger output. See Create a sandbox.
Run another command
Commands in the same sandbox share its filesystem. Give each command a unique
execId so you can reconnect to it after a connection loss:
curl "https://api.openrelay.inc/v1/sandboxes/$SANDBOX_ID/exec" \
-H "Authorization: Bearer $OPENRELAY_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"cmd": "python --version", "execId": "check-python-1"}'To reconnect, repeat the request with the same execId. For a stream, also pass
offset with the seq of the last event you received. This follows the existing
command instead of starting another. The host keeps a command's most recent 1 MiB
of output for reconnection; an older offset returns OUTPUT_EVICTED. A finished
command is kept while it is one of the sandbox's 16 most recent, and for up to 10
minutes after the sandbox ends.
See Run a command and
Open an interactive terminal.
Write and read files
File paths are absolute paths inside the sandbox. The parent directory must exist, and writing to an existing file replaces its contents.
printf 'Hello from OpenRelay\n' | \
curl -X PUT \
"https://api.openrelay.inc/v1/sandboxes/$SANDBOX_ID/files/content?path=/tmp/message.txt" \
-H "Authorization: Bearer $OPENRELAY_API_KEY" \
-H "Content-Type: application/octet-stream" \
--data-binary @-
curl --get \
"https://api.openrelay.inc/v1/sandboxes/$SANDBOX_ID/files/content" \
-H "Authorization: Bearer $OPENRELAY_API_KEY" \
--data-urlencode "path=/tmp/message.txt"Use GET /v1/sandboxes/{sandboxId}/files
with a dir query parameter to list a directory. See
Write a file and
Read a file for the full contract.
Expose an HTTP service
Start your service inside the sandbox, listening on its network interface, such
as 0.0.0.0:8000. Expose its port to get an HTTPS URL under
sandbox.openrelay.inc:
curl "https://api.openrelay.inc/v1/sandboxes/$SANDBOX_ID/ports" \
-H "Authorization: Bearer $OPENRELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"port": 8000, "access": "public"}'The response's url reaches that port. A public URL is accessible to anyone
who knows it. Use application authentication for private data, or, when signed
previews are enabled for your organization, send "access": "signed" and
"expiresInSeconds": 300 instead. A signed URL grants access to anyone holding
it until its expiry, which cannot exceed the sandbox's expiry.
See Expose a port, Issue a fresh signed URL, and Stop exposing a port.
Stop the sandbox
A sandbox expires after timeoutSeconds, which defaults to 300 seconds and
cannot exceed 86,400 seconds. Stop it earlier when you have finished, after
copying out any files you need:
curl -X DELETE \
"https://api.openrelay.inc/v1/sandboxes/$SANDBOX_ID" \
-H "Authorization: Bearer $OPENRELAY_API_KEY"A successful stop returns 204. Commands and file operations on a sandbox
that has ended return 410 SANDBOX_EXITED. Read its final state with
Get a sandbox; the organization list retains
sandbox history for 24 hours after they end. The default list shows active
sandboxes, including paused ones. Pass state=exited to list recent history.
Create with "ephemeral": true to omit history when the sandbox ends. Ended
ephemeral sandboxes and history older than 24 hours return 404 on a get.
Pause and resume, when enabled, preserve the sandbox's files and processes on its host. Open network connections do not survive a pause, and the sandbox's expiry continues to count down while paused.
Handle refusals
| Status | Meaning | Next step |
|---|---|---|
401 | The key is missing or invalid. | Check the bearer token. |
403 | The request lacks permission, or the account cannot create a sandbox. | Read the error code and the endpoint's required permission. |
404 | Sandbox access or the requested feature is unavailable, or the resource does not exist. | Check organization access and the resource ID. |
409 SANDBOX_PAUSED | The operation needs a running sandbox. | Resume it first. |
410 SANDBOX_EXITED | The sandbox has ended. | Create a new sandbox. |
422 IDEMPOTENCY_KEY_REUSED | A different body was sent with a previously used key. | Use the original body for a retry, or a new key for new work. |
429 | An organization or command-concurrency limit was reached. | Honor Retry-After and release unused sandboxes or commands. |
503 CAPACITY_EXHAUSTED | The requested capacity did not become available. | Honor Retry-After before retrying a single create. |
For group creation, streaming errors, template builds, and network-policy endpoints, see the Sandboxes API reference.
Pods and persistent storage
Bring-your-own-container GPU pods, what survives a stop, restart, crash or node loss, and how the /workspace volume is billed and retained.
Managed Kubernetes
Kubernetes clusters whose control plane we run, with whole GPU or CPU hosts as nodes. Create a cluster and a node pool, write a kubeconfig, and use kubectl.