Expose a port on a public URL
Give a port inside a sandbox a stable HTTPS URL on on.boat.dev, private with a token or public. Same as boat host.
curl --request POST \
--url https://boat.dev/api/v1/sandboxes/{sandboxId}/host \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"port": 32768,
"public": false,
"localhost": false,
"title": "<string>"
}
'import requests
url = "https://boat.dev/api/v1/sandboxes/{sandboxId}/host"
payload = {
"port": 32768,
"public": False,
"localhost": False,
"title": "<string>"
}
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({port: 32768, public: false, localhost: false, title: '<string>'})
};
fetch('https://boat.dev/api/v1/sandboxes/{sandboxId}/host', 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://boat.dev/api/v1/sandboxes/{sandboxId}/host",
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([
'port' => 32768,
'public' => false,
'localhost' => false,
'title' => '<string>'
]),
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://boat.dev/api/v1/sandboxes/{sandboxId}/host"
payload := strings.NewReader("{\n \"port\": 32768,\n \"public\": false,\n \"localhost\": false,\n \"title\": \"<string>\"\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://boat.dev/api/v1/sandboxes/{sandboxId}/host")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"port\": 32768,\n \"public\": false,\n \"localhost\": false,\n \"title\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://boat.dev/api/v1/sandboxes/{sandboxId}/host")
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 \"port\": 32768,\n \"public\": false,\n \"localhost\": false,\n \"title\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"type": "port.hosted",
"success": true,
"port": 3000,
"url": "https://swift-otter-9021-3000.on.boat.dev?_token=redacted",
"isProtected": true,
"access": "private"
}{
"ok": false,
"type": "sandbox.error",
"status": 400,
"code": "invalid_json",
"message": "Request body must be valid JSON.",
"error": {
"code": "invalid_json",
"message": "Request body must be valid JSON.",
"status": 400
},
"requestId": "req_01HX..."
}{
"ok": false,
"type": "sandbox.error",
"status": 401,
"code": "unauthorized",
"message": "Unauthorized",
"error": {
"code": "unauthorized",
"message": "Unauthorized",
"status": 401
},
"requestId": "req_01HX..."
}{
"ok": false,
"type": "sandbox.error",
"status": 409,
"code": "provider_not_configured",
"message": "Prompting is locked until Codex is configured on the Agents page.",
"requestId": "req_01HX...",
"error": {
"code": "<string>",
"message": "<string>",
"status": 123,
"details": {}
}
}{
"ok": false,
"type": "sandbox.error",
"status": 409,
"code": "provider_not_configured",
"message": "Prompting is locked until Codex is configured on the Agents page.",
"requestId": "req_01HX...",
"error": {
"code": "<string>",
"message": "<string>",
"status": 123,
"details": {}
}
}Authorizations
Boat bearer token in the form boat_.... Service API keys authenticate sandbox operations.
Path Parameters
Public Sandbox id returned by create/list/get sandbox calls.
^bx_[23456789abcdefghjkmnpqrstuvwxyz]{8}$Body
Port the service listens on inside the sandbox.
1 <= x <= 65535Return an ungated URL instead of one that requires the _token query parameter.
Set when the service listens on localhost (127.0.0.1 or ::1) only. The sandbox relays the URL to it. Leave unset for a service bound to 0.0.0.0. Each call sets the value again.
Display title for the hosted port.
Response
The public HTTPS URL for the port.
true
Stable success envelope discriminator added by v1.
"port.hosted"
Public HTTPS URL. Carries the _token query parameter unless the port is public; redact from logs.
private, public The sandbox relays the URL to a service that listens on localhost only.
curl --request POST \
--url https://boat.dev/api/v1/sandboxes/{sandboxId}/host \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"port": 32768,
"public": false,
"localhost": false,
"title": "<string>"
}
'import requests
url = "https://boat.dev/api/v1/sandboxes/{sandboxId}/host"
payload = {
"port": 32768,
"public": False,
"localhost": False,
"title": "<string>"
}
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({port: 32768, public: false, localhost: false, title: '<string>'})
};
fetch('https://boat.dev/api/v1/sandboxes/{sandboxId}/host', 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://boat.dev/api/v1/sandboxes/{sandboxId}/host",
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([
'port' => 32768,
'public' => false,
'localhost' => false,
'title' => '<string>'
]),
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://boat.dev/api/v1/sandboxes/{sandboxId}/host"
payload := strings.NewReader("{\n \"port\": 32768,\n \"public\": false,\n \"localhost\": false,\n \"title\": \"<string>\"\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://boat.dev/api/v1/sandboxes/{sandboxId}/host")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"port\": 32768,\n \"public\": false,\n \"localhost\": false,\n \"title\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://boat.dev/api/v1/sandboxes/{sandboxId}/host")
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 \"port\": 32768,\n \"public\": false,\n \"localhost\": false,\n \"title\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"type": "port.hosted",
"success": true,
"port": 3000,
"url": "https://swift-otter-9021-3000.on.boat.dev?_token=redacted",
"isProtected": true,
"access": "private"
}{
"ok": false,
"type": "sandbox.error",
"status": 400,
"code": "invalid_json",
"message": "Request body must be valid JSON.",
"error": {
"code": "invalid_json",
"message": "Request body must be valid JSON.",
"status": 400
},
"requestId": "req_01HX..."
}{
"ok": false,
"type": "sandbox.error",
"status": 401,
"code": "unauthorized",
"message": "Unauthorized",
"error": {
"code": "unauthorized",
"message": "Unauthorized",
"status": 401
},
"requestId": "req_01HX..."
}{
"ok": false,
"type": "sandbox.error",
"status": 409,
"code": "provider_not_configured",
"message": "Prompting is locked until Codex is configured on the Agents page.",
"requestId": "req_01HX...",
"error": {
"code": "<string>",
"message": "<string>",
"status": 123,
"details": {}
}
}{
"ok": false,
"type": "sandbox.error",
"status": 409,
"code": "provider_not_configured",
"message": "Prompting is locked until Codex is configured on the Agents page.",
"requestId": "req_01HX...",
"error": {
"code": "<string>",
"message": "<string>",
"status": 123,
"details": {}
}
}