Remote client API
Low-level remote-environment transport used by custom Letta Code clients.
The Remote client API is the low-level WebSocket transport for controlling a Letta Code remote environment.
For most applications, use the Letta Agent SDK instead. With backend: "cloud", the SDK manages the remote environment or Constellation sandbox, connects to the Remote client API, sends turns, streams events, and cleans up session state for you.
Use this API directly only when you are building a custom client, dashboard, router, or SDK-level integration that needs to own the remote WebSocket connection.
How it fits together
Section titled “How it fits together”A remote environment has three pieces:
| Piece | Meaning |
|---|---|
| Device | The machine, VM, container, or sandbox where Letta Code runs tools. |
| Connection | The current online WebSocket session for that device. Identified by connectionId. |
| Client | Your UI, app server, or router that sends commands and renders events. |
A device has a stable deviceId. A connection is temporary: it changes whenever the remote environment reconnects. Clients use the current connectionId to open a hosted status WebSocket.
Most scoped commands include the runtime you want to control:
{ "runtime": { "agent_id": "agent-...", "conversation_id": "conv-..." }}Hosted flow
Section titled “Hosted flow”1. Find an online environment
Section titled “1. Find an online environment”curl -sS 'https://api.letta.com/v1/environments?onlineOnly=true' \ -H "Authorization: Bearer $LETTA_API_KEY"Response:
{ "connections": [ { "connectionId": "conn-...", "deviceId": "device-abc123", "connectionName": "Work laptop", "currentMode": "standard", "metadata": { "workingDirectory": "/workspace/project", "gitBranch": "main" } } ], "hasNextPage": false}Pick an online connection and keep its connectionId.
2. Open the WebSocket
Section titled “2. Open the WebSocket”wss://api.letta.com/v1/environments/{connectionId}/status/ws?agentId={agentId}&conversationId={conversationId}&channel=streamAuthenticate with Authorization: Bearer <token> when your WebSocket client supports headers. Browser clients cannot set WebSocket headers, so browser integrations can pass token= in the URL instead.
3. Sync state
Section titled “3. Sync state”Send sync when the socket opens or reconnects:
{ "type": "sync", "runtime": { "agent_id": "agent-...", "conversation_id": "conv-..." }, "recover_approvals": true}The runtime responds with state events such as update_device_status, update_loop_status, and update_queue.
4. Send a turn
Section titled “4. Send a turn”{ "type": "input", "runtime": { "agent_id": "agent-...", "conversation_id": "conv-..." }, "payload": { "kind": "create_message", "messages": [ { "role": "user", "content": "What files should I inspect first?", "client_message_id": "cm_01J_demo" } ] }}Listen for stream_delta events for output and update_loop_status events for turn state. The turn is idle again when the runtime reports WAITING_ON_INPUT or emits a stop reason.
Minimal client
Section titled “Minimal client”const token = process.env.LETTA_API_KEY!;const connectionId = "conn-...";const agentId = "agent-...";const conversationId = "conv-...";
const url = new URL( `wss://api.letta.com/v1/environments/${connectionId}/status/ws`,);url.searchParams.set("agentId", agentId);url.searchParams.set("conversationId", conversationId);url.searchParams.set("channel", "stream");
const ws = new WebSocket(url, { headers: { Authorization: `Bearer ${token}` },});
function send(frame: unknown) { ws.send(JSON.stringify(frame));}
ws.addEventListener("open", () => { const runtime = { agent_id: agentId, conversation_id: conversationId };
send({ type: "sync", runtime, recover_approvals: true }); send({ type: "input", runtime, payload: { kind: "create_message", messages: [{ role: "user", content: "Summarize this repo." }], }, });});
ws.addEventListener("message", (event) => { const frame = JSON.parse(event.data);
if (typeof frame.seq === "number") { send({ type: "ack", seq: frame.seq }); }
if (frame.type === "stream_delta") { renderDelta(frame.delta); }
if (frame.type === "update_loop_status") { renderStatus(frame.loop_status); }});Protocol references
Section titled “Protocol references”The Remote client API shares the same command and event vocabulary as App Server. Use these references instead of duplicating protocol details here:
- Remote client API reference - Hosted environment discovery, auth, WebSocket URL, and transport ACKs.
- App Server protocol lifecycle - Canonical
input,sync,abort_message, streaming, approvals, and device capability commands. - Self-hosted remotes - Direct custom transport when you do not want the hosted environment router.
Skip to content