curl --request POST \
--url https://sailbox-api.sailresearch.com/v1/sailboxes/from_checkpoint \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"checkpoint_id": "sbcp_7a1b9c4d-2e5f-4a6b-9c8d-3e1f2a4b6c8d",
"name": "worker-restored"
}
'import requests
url = "https://sailbox-api.sailresearch.com/v1/sailboxes/from_checkpoint"
payload = {
"checkpoint_id": "sbcp_7a1b9c4d-2e5f-4a6b-9c8d-3e1f2a4b6c8d",
"name": "worker-restored"
}
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({
checkpoint_id: 'sbcp_7a1b9c4d-2e5f-4a6b-9c8d-3e1f2a4b6c8d',
name: 'worker-restored'
})
};
fetch('https://sailbox-api.sailresearch.com/v1/sailboxes/from_checkpoint', 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/from_checkpoint",
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([
'checkpoint_id' => 'sbcp_7a1b9c4d-2e5f-4a6b-9c8d-3e1f2a4b6c8d',
'name' => 'worker-restored'
]),
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/from_checkpoint"
payload := strings.NewReader("{\n \"checkpoint_id\": \"sbcp_7a1b9c4d-2e5f-4a6b-9c8d-3e1f2a4b6c8d\",\n \"name\": \"worker-restored\"\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/from_checkpoint")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"checkpoint_id\": \"sbcp_7a1b9c4d-2e5f-4a6b-9c8d-3e1f2a4b6c8d\",\n \"name\": \"worker-restored\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://sailbox-api.sailresearch.com/v1/sailboxes/from_checkpoint")
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 \"checkpoint_id\": \"sbcp_7a1b9c4d-2e5f-4a6b-9c8d-3e1f2a4b6c8d\",\n \"name\": \"worker-restored\"\n}"
response = http.request(request)
puts response.read_body{
"sailbox_id": "sb_2f7a4b18-9d6c-4e03-b512-8a3f7c1d9e04",
"name": "worker-restored",
"status": "running",
"checkpoint_id": "sbcp_7a1b9c4d-2e5f-4a6b-9c8d-3e1f2a4b6c8d",
"source_checkpoint_generation": 3
}{
"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": "idempotency key reused with a different request body",
"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
}
}Start a Sailbox from a checkpoint
Creates a new Sailbox from a checkpoint you took earlier. It comes up with the memory and filesystem saved in the checkpoint, so processes the original was running carry on there.
Commands run through an SDK or the CLI stop there. Everything they had written by then is on the filesystem, and one started with their background option keeps running. Start whatever else you need running again. Any Sailbox can come up cold, with the disk intact and nothing running, so write code that expects it.
Volumes mounted on the original Sailbox are mounted on the new one at the same paths. They are the same volumes, not copies, so both Sailboxes read and write the same files. The new Sailbox keeps the original’s egress policy: its reachability settings and its request rules alike.
curl --request POST \
--url https://sailbox-api.sailresearch.com/v1/sailboxes/from_checkpoint \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"checkpoint_id": "sbcp_7a1b9c4d-2e5f-4a6b-9c8d-3e1f2a4b6c8d",
"name": "worker-restored"
}
'import requests
url = "https://sailbox-api.sailresearch.com/v1/sailboxes/from_checkpoint"
payload = {
"checkpoint_id": "sbcp_7a1b9c4d-2e5f-4a6b-9c8d-3e1f2a4b6c8d",
"name": "worker-restored"
}
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({
checkpoint_id: 'sbcp_7a1b9c4d-2e5f-4a6b-9c8d-3e1f2a4b6c8d',
name: 'worker-restored'
})
};
fetch('https://sailbox-api.sailresearch.com/v1/sailboxes/from_checkpoint', 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/from_checkpoint",
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([
'checkpoint_id' => 'sbcp_7a1b9c4d-2e5f-4a6b-9c8d-3e1f2a4b6c8d',
'name' => 'worker-restored'
]),
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/from_checkpoint"
payload := strings.NewReader("{\n \"checkpoint_id\": \"sbcp_7a1b9c4d-2e5f-4a6b-9c8d-3e1f2a4b6c8d\",\n \"name\": \"worker-restored\"\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/from_checkpoint")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"checkpoint_id\": \"sbcp_7a1b9c4d-2e5f-4a6b-9c8d-3e1f2a4b6c8d\",\n \"name\": \"worker-restored\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://sailbox-api.sailresearch.com/v1/sailboxes/from_checkpoint")
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 \"checkpoint_id\": \"sbcp_7a1b9c4d-2e5f-4a6b-9c8d-3e1f2a4b6c8d\",\n \"name\": \"worker-restored\"\n}"
response = http.request(request)
puts response.read_body{
"sailbox_id": "sb_2f7a4b18-9d6c-4e03-b512-8a3f7c1d9e04",
"name": "worker-restored",
"status": "running",
"checkpoint_id": "sbcp_7a1b9c4d-2e5f-4a6b-9c8d-3e1f2a4b6c8d",
"source_checkpoint_generation": 3
}{
"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": "idempotency key reused with a different request body",
"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
Makes the request retry-safe. Sail remembers the answer it sent under a key, including a 400 or a 409, and replays it with Idempotent-Replayed: true for a retry that repeats the key, the method, the path, and the body. Bodies are compared byte for byte. Reusing a key for a different request returns 409, so send a corrected request under a new key. A key that is blank or only spaces is ignored, and the request runs without idempotency.
Any string of up to 255 bytes works, and characters outside ASCII count for more than one. A UUID is a good default. Keys are remembered for at least 24 hours and scoped to the API key that sent them.
A server error, or a failure to record the answer, can leave a key unsettled, and creating a Sailbox is where that matters. See Retrying safely.
255Body
Checkpoint to start from.
1\STerminate this Sailbox after this many wall-clock seconds from creation, including time spent waiting for capacity, sleeping, or paused. Image preparation happens before creation. Omit or pass null for unlimited lifetime. The deadline cannot be extended. Expiration is checked periodically, so termination can occur after the deadline. A new Sailbox started from a checkpoint gets its own limit; it does not inherit the source Sailbox's deadline.
1 <= x <= 4294967295Name for the new Sailbox. Defaults to a name derived from the Sailbox the checkpoint came from. Control characters are not allowed.
128Response
The new Sailbox is running. One that cannot be brought up fails the request instead of answering 200.
Id of the new Sailbox.
Current state of the Sailbox. Values in use today are creating, running, paused, sleeping, terminating, terminated, failed, create_failed, interrupted_restorable, and interrupted_unsafe_to_retry. New values can appear over time, so treat this as an open set.
Checkpoint it was started from.
Checkpoint counter of the Sailbox the checkpoint came from, as of that checkpoint. It goes up by one every time the Sailbox saves its state, which includes pausing and sleeping.
Name of the new Sailbox.