Skip to content
OpenRelay is in early access, and the /v1 API is stable. New capabilities ship in the changelog.
Skip to Content
GuidesCompute

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

StatusMeaningNext step
401The key is missing or invalid.Check the bearer token.
403The request lacks permission, or the account cannot create a sandbox.Read the error code and the endpoint's required permission.
404Sandbox access or the requested feature is unavailable, or the resource does not exist.Check organization access and the resource ID.
409 SANDBOX_PAUSEDThe operation needs a running sandbox.Resume it first.
410 SANDBOX_EXITEDThe sandbox has ended.Create a new sandbox.
422 IDEMPOTENCY_KEY_REUSEDA different body was sent with a previously used key.Use the original body for a retry, or a new key for new work.
429An organization or command-concurrency limit was reached.Honor Retry-After and release unused sandboxes or commands.
503 CAPACITY_EXHAUSTEDThe 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.

On this page