curl --request POST \
--url https://odyssey.asteroid.ai/agents/v2/environments \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: <api-key>' \
--data '
{
"organizationId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"agentId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"agentProfileId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"source": {
"kind": "workflow",
"workflowId": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}
'import requests
url = "https://odyssey.asteroid.ai/agents/v2/environments"
payload = {
"organizationId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"agentId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"agentProfileId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"source": {
"kind": "workflow",
"workflowId": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}
headers = {
"X-Api-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-Api-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
organizationId: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
agentId: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
agentProfileId: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
source: {kind: 'workflow', workflowId: '3c90c3cc-0d44-4b50-8888-8dd25736052a'}
})
};
fetch('https://odyssey.asteroid.ai/agents/v2/environments', 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://odyssey.asteroid.ai/agents/v2/environments",
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([
'organizationId' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'agentId' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'agentProfileId' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'source' => [
'kind' => 'workflow',
'workflowId' => '3c90c3cc-0d44-4b50-8888-8dd25736052a'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Api-Key: <api-key>"
],
]);
$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://odyssey.asteroid.ai/agents/v2/environments"
payload := strings.NewReader("{\n \"organizationId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"agentId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"agentProfileId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"source\": {\n \"kind\": \"workflow\",\n \"workflowId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Api-Key", "<api-key>")
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://odyssey.asteroid.ai/agents/v2/environments")
.header("X-Api-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"organizationId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"agentId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"agentProfileId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"source\": {\n \"kind\": \"workflow\",\n \"workflowId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://odyssey.asteroid.ai/agents/v2/environments")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Api-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"organizationId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"agentId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"agentProfileId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"source\": {\n \"kind\": \"workflow\",\n \"workflowId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n }\n}"
response = http.request(request)
puts response.read_body{
"connection": {
"cdpUrl": "<string>"
},
"createdAt": "2023-11-07T05:31:56Z",
"environmentType": "browser",
"expiresAt": "2023-11-07T05:31:56Z",
"hasRecording": true,
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"organizationId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"owner": {
"agentId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"executionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "execution"
},
"state": {
"environmentType": "browser",
"hasRecording": true,
"height": 123,
"provider": "anchor",
"width": 123,
"browserIp": "<string>",
"liveViewUrl": "<string>",
"providerConfig": {
"type": "anchor",
"sessionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"sessionUrl": "<string>"
},
"recordingStartedAt": "2023-11-07T05:31:56Z",
"recordingUrl": "<string>"
},
"status": "requested",
"agentProfileId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"osType": "linux",
"readyAt": "2023-11-07T05:31:56Z",
"stoppedAt": "2023-11-07T05:31:56Z"
}{
"code": 400,
"message": "<string>"
}{
"code": 401,
"message": "<string>"
}{
"code": 403,
"message": "<string>"
}{
"code": 404,
"message": "<string>"
}{
"code": 429,
"message": "<string>"
}{
"code": 500,
"message": "<string>"
}Start environment
Start an environment. Blocks until it is ready (or the provider fails) and returns the environment together with its connection details, so a caller boots in one round trip. The organisation in the body is the scope; the agent, when given, is a binding the server verifies belongs to it.
curl --request POST \
--url https://odyssey.asteroid.ai/agents/v2/environments \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: <api-key>' \
--data '
{
"organizationId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"agentId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"agentProfileId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"source": {
"kind": "workflow",
"workflowId": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}
'import requests
url = "https://odyssey.asteroid.ai/agents/v2/environments"
payload = {
"organizationId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"agentId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"agentProfileId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"source": {
"kind": "workflow",
"workflowId": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}
headers = {
"X-Api-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-Api-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
organizationId: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
agentId: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
agentProfileId: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
source: {kind: 'workflow', workflowId: '3c90c3cc-0d44-4b50-8888-8dd25736052a'}
})
};
fetch('https://odyssey.asteroid.ai/agents/v2/environments', 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://odyssey.asteroid.ai/agents/v2/environments",
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([
'organizationId' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'agentId' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'agentProfileId' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'source' => [
'kind' => 'workflow',
'workflowId' => '3c90c3cc-0d44-4b50-8888-8dd25736052a'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Api-Key: <api-key>"
],
]);
$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://odyssey.asteroid.ai/agents/v2/environments"
payload := strings.NewReader("{\n \"organizationId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"agentId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"agentProfileId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"source\": {\n \"kind\": \"workflow\",\n \"workflowId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Api-Key", "<api-key>")
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://odyssey.asteroid.ai/agents/v2/environments")
.header("X-Api-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"organizationId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"agentId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"agentProfileId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"source\": {\n \"kind\": \"workflow\",\n \"workflowId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://odyssey.asteroid.ai/agents/v2/environments")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Api-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"organizationId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"agentId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"agentProfileId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"source\": {\n \"kind\": \"workflow\",\n \"workflowId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n }\n}"
response = http.request(request)
puts response.read_body{
"connection": {
"cdpUrl": "<string>"
},
"createdAt": "2023-11-07T05:31:56Z",
"environmentType": "browser",
"expiresAt": "2023-11-07T05:31:56Z",
"hasRecording": true,
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"organizationId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"owner": {
"agentId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"executionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "execution"
},
"state": {
"environmentType": "browser",
"hasRecording": true,
"height": 123,
"provider": "anchor",
"width": 123,
"browserIp": "<string>",
"liveViewUrl": "<string>",
"providerConfig": {
"type": "anchor",
"sessionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"sessionUrl": "<string>"
},
"recordingStartedAt": "2023-11-07T05:31:56Z",
"recordingUrl": "<string>"
},
"status": "requested",
"agentProfileId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"osType": "linux",
"readyAt": "2023-11-07T05:31:56Z",
"stoppedAt": "2023-11-07T05:31:56Z"
}{
"code": 400,
"message": "<string>"
}{
"code": 401,
"message": "<string>"
}{
"code": 403,
"message": "<string>"
}{
"code": 404,
"message": "<string>"
}{
"code": 429,
"message": "<string>"
}{
"code": 500,
"message": "<string>"
}Authorizations
Body
Request to start an environment. An environment belongs to an organisation; the agent is an optional binding carried in the body, not a path segment. With an agent, the environment is stamped external-owned and the agent's workflow/profile drive provisioning; without one, it is organization-owned and provisions from the explicit spec alone.
The organisation the environment belongs to. Required even when an agent is given — the server verifies the agent belongs to it, so a mismatched binding fails instead of stamping the wrong organisation.
The agent to bind the environment to. Gates access to that agent's workflow. Omit for an organization-owned environment with no agent binding.
Optional agent profile to attach. The profile id is stamped onto the env row for the session's lifetime, so later reads resolve against the stamped profile rather than the agent's current one. Its decrypted credentials come back on the response's connection only for service-to-service callers — they are never returned to an API key or a browser.
Where the environment spec comes from. Omit for the server default (a browser env at the computer-use resolution). The workflow arm needs an agent to resolve against, so it requires agentId.
- Option 1
- Option 2
Show child attributes
Show child attributes
Response
The request has succeeded.
A single environment: what it is, its provider state, and how to drive it. state is the same shape the execution endpoint returns, so the LiveView/OsLiveView components render either without branching.
How to connect to it.
Show child attributes
Show child attributes
When the environment row was created.
Browser or OS.
browser, os When the reaper will tear the environment down if no graceful Stop arrives first.
Whether a playable recording has been persisted (GCS object or provider URL). The playable URL itself is minted on the by-id read.
Environment identifier.
The organisation the environment belongs to.
Who the environment belongs to, and the identity that owner carries.
- Option 1
- Option 2
- Option 3
- Option 4
- Option 5
Show child attributes
Show child attributes
Environment state (browser or OS): live-view URL, viewport, provider config and a freshly-minted recording URL when one exists.
- Option 1
- Option 2
Show child attributes
Show child attributes
Current status, terminal states included. Lists exclude terminal environments unless includeTerminal is set.
requested, provisioning, ready, stopping, stopped, failed, dead Profile snapshot the environment was booted against, if any.
Operating system for OS environments. Absent for browser environments; linux when an OS environment predates explicit osType storage.
linux, windows When the environment first became ready, if it reached that state.
When the environment reached a terminal state. Set after Stop runs.

