curl --request POST \
--url https://sailbox-api.sailresearch.com/v1/sailboxes/{sailbox_id}/exec \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"command": "<string>",
"timeout": 2,
"background": false,
"cwd": "<string>",
"open_stdin": false,
"pty": false,
"term": "<string>",
"cols": 1,
"rows": 1,
"env": {},
"user": "<string>",
"idempotency_key": "<string>",
"stdout_resume_seq": 1,
"stderr_resume_seq": 1,
"forward_ports": false,
"forward_browser": false,
"forward_clipboard": false
}
'import requests
url = "https://sailbox-api.sailresearch.com/v1/sailboxes/{sailbox_id}/exec"
payload = {
"command": "<string>",
"timeout": 2,
"background": False,
"cwd": "<string>",
"open_stdin": False,
"pty": False,
"term": "<string>",
"cols": 1,
"rows": 1,
"env": {},
"user": "<string>",
"idempotency_key": "<string>",
"stdout_resume_seq": 1,
"stderr_resume_seq": 1,
"forward_ports": False,
"forward_browser": False,
"forward_clipboard": False
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
command: '<string>',
timeout: 2,
background: false,
cwd: '<string>',
open_stdin: false,
pty: false,
term: '<string>',
cols: 1,
rows: 1,
env: {},
user: '<string>',
idempotency_key: '<string>',
stdout_resume_seq: 1,
stderr_resume_seq: 1,
forward_ports: false,
forward_browser: false,
forward_clipboard: false
})
};
fetch('https://sailbox-api.sailresearch.com/v1/sailboxes/{sailbox_id}/exec', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://sailbox-api.sailresearch.com/v1/sailboxes/{sailbox_id}/exec",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'command' => '<string>',
'timeout' => 2,
'background' => false,
'cwd' => '<string>',
'open_stdin' => false,
'pty' => false,
'term' => '<string>',
'cols' => 1,
'rows' => 1,
'env' => [
],
'user' => '<string>',
'idempotency_key' => '<string>',
'stdout_resume_seq' => 1,
'stderr_resume_seq' => 1,
'forward_ports' => false,
'forward_browser' => false,
'forward_clipboard' => false
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://sailbox-api.sailresearch.com/v1/sailboxes/{sailbox_id}/exec"
payload := strings.NewReader("{\n \"command\": \"<string>\",\n \"timeout\": 2,\n \"background\": false,\n \"cwd\": \"<string>\",\n \"open_stdin\": false,\n \"pty\": false,\n \"term\": \"<string>\",\n \"cols\": 1,\n \"rows\": 1,\n \"env\": {},\n \"user\": \"<string>\",\n \"idempotency_key\": \"<string>\",\n \"stdout_resume_seq\": 1,\n \"stderr_resume_seq\": 1,\n \"forward_ports\": false,\n \"forward_browser\": false,\n \"forward_clipboard\": false\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://sailbox-api.sailresearch.com/v1/sailboxes/{sailbox_id}/exec")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"command\": \"<string>\",\n \"timeout\": 2,\n \"background\": false,\n \"cwd\": \"<string>\",\n \"open_stdin\": false,\n \"pty\": false,\n \"term\": \"<string>\",\n \"cols\": 1,\n \"rows\": 1,\n \"env\": {},\n \"user\": \"<string>\",\n \"idempotency_key\": \"<string>\",\n \"stdout_resume_seq\": 1,\n \"stderr_resume_seq\": 1,\n \"forward_ports\": false,\n \"forward_browser\": false,\n \"forward_clipboard\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://sailbox-api.sailresearch.com/v1/sailboxes/{sailbox_id}/exec")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"command\": \"<string>\",\n \"timeout\": 2,\n \"background\": false,\n \"cwd\": \"<string>\",\n \"open_stdin\": false,\n \"pty\": false,\n \"term\": \"<string>\",\n \"cols\": 1,\n \"rows\": 1,\n \"env\": {},\n \"user\": \"<string>\",\n \"idempotency_key\": \"<string>\",\n \"stdout_resume_seq\": 1,\n \"stderr_resume_seq\": 1,\n \"forward_ports\": false,\n \"forward_browser\": false,\n \"forward_clipboard\": false\n}"
response = http.request(request)
puts response.read_body"{\"type\":\"started\",\"exec_request_id\":\"exec_123\"}\n{\"type\":\"stdout\",\"data\":\"aGkK\",\"seq\":1}\n{\"type\":\"exit\",\"status\":\"succeeded\",\"return_code\":0}\n"{
"error": {
"message": "memory_limit_gib for size m must be between 8 and 128",
"type": "invalid_request_error",
"param": null,
"code": null
}
}{
"error": {
"message": "Invalid API key",
"type": "authentication_error",
"param": null,
"code": "invalid_api_key"
}
}{
"error": {
"message": "Your API key has been disabled due to insufficient credits. Visit https://app.sailresearch.com/billing to add credits.",
"type": "billing_error",
"param": null,
"code": "credits_exhausted",
"billing_url": "https://app.sailresearch.com/billing"
}
}{
"error": {
"message": "sailboxes require an organization-scoped API key",
"type": "permission_error",
"param": null,
"code": null
}
}{
"error": {
"message": "sailbox \"sb_9c8f1e2a-3b4d-4f5a-8c7e-1d2f3a4b5c6d\" not found",
"type": "not_found_error",
"param": null,
"code": null
}
}{
"error": {
"message": "sailbox listeners require persistence",
"type": "conflict_error",
"param": null,
"code": null
}
}{
"error": {
"message": "request body too large",
"type": "invalid_request_error",
"param": null,
"code": null
}
}{
"error": {
"message": "Too many concurrent requests. Please retry after some of your organization's in-flight requests complete.",
"type": "rate_limit_error",
"param": null,
"code": "rate_limited"
}
}{
"error": {
"message": "failed to fetch sailbox",
"type": "server_error",
"param": null,
"code": null
}
}{
"error": {
"message": "Authentication service unavailable",
"type": "server_error",
"param": null,
"code": null
}
}{
"error": {
"message": "idempotent request still in flight",
"type": "server_error",
"param": null,
"code": null
}
}Run a command
Starts a command or reconnects to one with the same idempotency key. The response is newline-delimited JSON. Its first event is started. Output data and terminal repaint data are base64. The highest stdout and stderr seq values are reconnect cursors. exit is terminal. A heartbeat can appear after 30 seconds without another event.
curl --request POST \
--url https://sailbox-api.sailresearch.com/v1/sailboxes/{sailbox_id}/exec \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"command": "<string>",
"timeout": 2,
"background": false,
"cwd": "<string>",
"open_stdin": false,
"pty": false,
"term": "<string>",
"cols": 1,
"rows": 1,
"env": {},
"user": "<string>",
"idempotency_key": "<string>",
"stdout_resume_seq": 1,
"stderr_resume_seq": 1,
"forward_ports": false,
"forward_browser": false,
"forward_clipboard": false
}
'import requests
url = "https://sailbox-api.sailresearch.com/v1/sailboxes/{sailbox_id}/exec"
payload = {
"command": "<string>",
"timeout": 2,
"background": False,
"cwd": "<string>",
"open_stdin": False,
"pty": False,
"term": "<string>",
"cols": 1,
"rows": 1,
"env": {},
"user": "<string>",
"idempotency_key": "<string>",
"stdout_resume_seq": 1,
"stderr_resume_seq": 1,
"forward_ports": False,
"forward_browser": False,
"forward_clipboard": False
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
command: '<string>',
timeout: 2,
background: false,
cwd: '<string>',
open_stdin: false,
pty: false,
term: '<string>',
cols: 1,
rows: 1,
env: {},
user: '<string>',
idempotency_key: '<string>',
stdout_resume_seq: 1,
stderr_resume_seq: 1,
forward_ports: false,
forward_browser: false,
forward_clipboard: false
})
};
fetch('https://sailbox-api.sailresearch.com/v1/sailboxes/{sailbox_id}/exec', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://sailbox-api.sailresearch.com/v1/sailboxes/{sailbox_id}/exec",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'command' => '<string>',
'timeout' => 2,
'background' => false,
'cwd' => '<string>',
'open_stdin' => false,
'pty' => false,
'term' => '<string>',
'cols' => 1,
'rows' => 1,
'env' => [
],
'user' => '<string>',
'idempotency_key' => '<string>',
'stdout_resume_seq' => 1,
'stderr_resume_seq' => 1,
'forward_ports' => false,
'forward_browser' => false,
'forward_clipboard' => false
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://sailbox-api.sailresearch.com/v1/sailboxes/{sailbox_id}/exec"
payload := strings.NewReader("{\n \"command\": \"<string>\",\n \"timeout\": 2,\n \"background\": false,\n \"cwd\": \"<string>\",\n \"open_stdin\": false,\n \"pty\": false,\n \"term\": \"<string>\",\n \"cols\": 1,\n \"rows\": 1,\n \"env\": {},\n \"user\": \"<string>\",\n \"idempotency_key\": \"<string>\",\n \"stdout_resume_seq\": 1,\n \"stderr_resume_seq\": 1,\n \"forward_ports\": false,\n \"forward_browser\": false,\n \"forward_clipboard\": false\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://sailbox-api.sailresearch.com/v1/sailboxes/{sailbox_id}/exec")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"command\": \"<string>\",\n \"timeout\": 2,\n \"background\": false,\n \"cwd\": \"<string>\",\n \"open_stdin\": false,\n \"pty\": false,\n \"term\": \"<string>\",\n \"cols\": 1,\n \"rows\": 1,\n \"env\": {},\n \"user\": \"<string>\",\n \"idempotency_key\": \"<string>\",\n \"stdout_resume_seq\": 1,\n \"stderr_resume_seq\": 1,\n \"forward_ports\": false,\n \"forward_browser\": false,\n \"forward_clipboard\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://sailbox-api.sailresearch.com/v1/sailboxes/{sailbox_id}/exec")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"command\": \"<string>\",\n \"timeout\": 2,\n \"background\": false,\n \"cwd\": \"<string>\",\n \"open_stdin\": false,\n \"pty\": false,\n \"term\": \"<string>\",\n \"cols\": 1,\n \"rows\": 1,\n \"env\": {},\n \"user\": \"<string>\",\n \"idempotency_key\": \"<string>\",\n \"stdout_resume_seq\": 1,\n \"stderr_resume_seq\": 1,\n \"forward_ports\": false,\n \"forward_browser\": false,\n \"forward_clipboard\": false\n}"
response = http.request(request)
puts response.read_body"{\"type\":\"started\",\"exec_request_id\":\"exec_123\"}\n{\"type\":\"stdout\",\"data\":\"aGkK\",\"seq\":1}\n{\"type\":\"exit\",\"status\":\"succeeded\",\"return_code\":0}\n"{
"error": {
"message": "memory_limit_gib for size m must be between 8 and 128",
"type": "invalid_request_error",
"param": null,
"code": null
}
}{
"error": {
"message": "Invalid API key",
"type": "authentication_error",
"param": null,
"code": "invalid_api_key"
}
}{
"error": {
"message": "Your API key has been disabled due to insufficient credits. Visit https://app.sailresearch.com/billing to add credits.",
"type": "billing_error",
"param": null,
"code": "credits_exhausted",
"billing_url": "https://app.sailresearch.com/billing"
}
}{
"error": {
"message": "sailboxes require an organization-scoped API key",
"type": "permission_error",
"param": null,
"code": null
}
}{
"error": {
"message": "sailbox \"sb_9c8f1e2a-3b4d-4f5a-8c7e-1d2f3a4b5c6d\" not found",
"type": "not_found_error",
"param": null,
"code": null
}
}{
"error": {
"message": "sailbox listeners require persistence",
"type": "conflict_error",
"param": null,
"code": null
}
}{
"error": {
"message": "request body too large",
"type": "invalid_request_error",
"param": null,
"code": null
}
}{
"error": {
"message": "Too many concurrent requests. Please retry after some of your organization's in-flight requests complete.",
"type": "rate_limit_error",
"param": null,
"code": "rate_limited"
}
}{
"error": {
"message": "failed to fetch sailbox",
"type": "server_error",
"param": null,
"code": null
}
}{
"error": {
"message": "Authentication service unavailable",
"type": "server_error",
"param": null,
"code": null
}
}{
"error": {
"message": "idempotent request still in flight",
"type": "server_error",
"param": null,
"code": null
}
}Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Headers
Header-compatible exec id for retries and reconnects. It must match idempotency_key when both are sent. Use the JSON field for spaces, Unicode, or URL-reserved characters.
1Lets an organization admin operate a private Sailbox someone else created. Say why in plain text; the reason goes into your organization's audit log, and the operation is refused without it. Ignored on Sailboxes you could already operate.
Pause, resume, sleep, scheduled wake, upgrade, and terminate accept an override. Attaching or clearing an HTTP policy accepts one too. Running commands and moving files accept one through an SDK or the CLI. Publishing and unpublishing ports, adding and removing custom domains, checkpointing, and creating from a checkpoint stay with the creator.
^[ -~]*$Path Parameters
Id of the Sailbox, as returned by create. It is sb_ followed by a UUID.
Body
Shell command string or argument array. Shell features apply only to a string.
1Command timeout in seconds.
x >= 1Detach a string command from standard input and output.
Working directory for a string command.
Keep standard input open for writes.
Run the command in a pseudo-terminal. This implies open_stdin.
Initial TERM value. A PTY defaults to xterm-256color.
Initial PTY width. Zero uses the guest default.
x >= 0Initial PTY height. Zero uses the guest default.
x >= 0Environment values for the command. Names must match [A-Za-z_][A-Za-z0-9_]*. Names and values cannot contain a NUL character.
Show child attributes
Show child attributes
Guest user in the same form as the SDK user option.
Stable exec id for a retry or stream reconnect. It can contain spaces, Unicode, and URL-reserved characters. Sail trims whitespace from its ends. The trimmed UTF-8 value can be up to 256 KiB.
Highest stdout sequence already received.
x >= 0Highest stderr sequence already received.
x >= 0Response
Exec event stream.
The response is of type string.