Remote Client API Reference
Reference for Letta Code remote client WebSocket commands, responses, and events.
Use this reference after the Remote Client API quick start.
Authentication
Section titled “Authentication”Use an Authorization header when your WebSocket client supports headers:
Authorization: Bearer <token>Browser clients cannot set WebSocket headers. For browsers, put the token in the URL:
?token=<token>REST endpoints
Section titled “REST endpoints”List environments
Section titled “List environments”Use this to find an online Letta Code environment and get its connectionId.
Request
GET /v1/environments?onlineOnly=trueAuthorization: Bearer $LETTA_API_KEYResponse
{ "connections": [ { "id": "env-db-id", "connectionId": "conn-...", "deviceId": "device-abc123", "connectionName": "Work laptop", "organizationId": "org-...", "connectedAt": 1780950000000, "lastHeartbeat": 1780950030000, "lastSeenAt": 1780950000000, "firstSeenAt": 1780940000000, "currentMode": "standard", "metadata": { "workingDirectory": "/workspace/project", "gitBranch": "main" } } ], "hasNextPage": false}Use connectionId to open the status WebSocket.
WebSocket endpoint
Section titled “WebSocket endpoint”Connect to the status WebSocket:
wss://api.letta.com/v1/environments/{connectionId}/status/ws?agentId={agentId}&conversationId={conversationId}&channel=streamBrowser fallback with query-token auth:
wss://api.letta.com/v1/environments/{connectionId}/status/ws?agentId={agentId}&conversationId={conversationId}&channel=stream&token={token}Shared fields
Section titled “Shared fields”| Field | Direction | Meaning |
|---|---|---|
type | Both | Message type. Commands and events are distinguished by type. |
request_id | Client → device, device → client | Client-generated ID for request/response matching. |
runtime.agent_id | Client → device | Persistent Letta agent ID. |
runtime.conversation_id | Client → device | Conversation/thread ID. |
seq | Server → client | Transport sequence number. ACK with { "type": "ack", "seq": n }. |
event_seq | Device → client | Device event sequence number. Use to detect missed events. |
idempotency_key | Device → client | Event dedupe key. |
Response events
Section titled “Response events”Most one-shot commands return a response event with the same request_id. Match
responses to requests by request_id.
Filesystem responses:
| Send command | Receive event |
|---|---|
list_in_directory | list_in_directory_response |
get_tree | get_tree_response |
read_file | read_file_response |
write_file | write_file_response |
edit_file | edit_file_response |
search_files | search_files_response |
grep_in_files | grep_in_files_response |
Memory and MemFS responses:
| Send command | Receive event |
|---|---|
list_memory | list_memory_response |
memory_history | memory_history_response |
memory_file_at_ref | memory_file_at_ref_response |
memory_commit_diff | memory_commit_diff_response |
read_memory_file | read_memory_file_response |
write_memory_file | write_memory_file_response |
delete_memory_file | delete_memory_file_response |
enable_memfs | enable_memfs_response |
Model and provider responses:
| Send command | Receive event |
|---|---|
list_models | list_models_response |
update_model | update_model_response |
list_connect_providers | list_connect_providers_response |
connect_provider | connect_provider_response |
disconnect_provider | disconnect_provider_response |
Git, experiments, secrets, and skills responses:
| Send command | Receive event |
|---|---|
search_branches | search_branches_response |
checkout_branch | checkout_branch_response |
get_experiments | get_experiments_response |
set_experiment | set_experiment_response |
secret_list | secret_list_response |
secret_apply | secret_apply_response |
skill_enable | skill_enable_response |
skill_disable | skill_disable_response |
Channels responses:
| Send command | Receive event |
|---|---|
channels_list | channels_list_response |
channel_accounts_list | channel_accounts_list_response |
channel_account_create | channel_account_create_response |
channel_account_update | channel_account_update_response |
channel_account_bind | channel_account_bind_response |
channel_account_unbind | channel_account_unbind_response |
channel_account_delete | channel_account_delete_response |
channel_account_start | channel_account_start_response |
channel_account_stop | channel_account_stop_response |
channel_get_config | channel_get_config_response |
channel_set_config | channel_set_config_response |
channel_start | channel_start_response |
channel_stop | channel_stop_response |
channel_pairings_list | channel_pairings_list_response |
channel_pairing_bind | channel_pairing_bind_response |
channel_routes_list | channel_routes_list_response |
channel_targets_list | channel_targets_list_response |
channel_target_bind | channel_target_bind_response |
channel_route_remove | channel_route_remove_response |
Other acknowledgements and non-_response replies:
| Send command | Receive event |
|---|---|
ping | pong |
abort_message | cancel_ack |
change_cwd | cwd_changed |
recover_pending_approvals | recover_pending_approvals_ack |
sync | State events such as update_device_status, update_loop_status, and update_queue |
input with create_message | Streaming events such as update_loop_status, stream_delta, and control_request |
change_device_state | State events such as update_device_status |
Commands
Section titled “Commands”Keep the status WebSocket alive.
Send
{ "type": "ping" }Receive
{ "type": "pong", "timestamp": 1780950030000 }Acknowledge any server message that includes seq.
Send
{ "type": "ack", "seq": 42 }Receive
No direct response.
Ask the device to replay current state for the runtime.
Send
{ "type": "sync", "runtime": { "agent_id": "agent-...", "conversation_id": "conv-..." }, "recover_approvals": true}Receive
update_device_statusupdate_loop_statusupdate_queuecontrol_requestor pending approvals, whenrecover_approvalsistrue
input with create_message
Section titled “input with create_message”Send a user message and start a streaming turn.
Send
{ "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" } ], "supports_control_response": true }}Receive
update_loop_statusstream_deltacontrol_requestupdate_queueerrororrun_request_error
The turn is idle when update_loop_status.loop_status.status is WAITING_ON_INPUT.
input with approval_response
Section titled “input with approval_response”Approve or deny a tool request.
Send allow
{ "type": "input", "runtime": { "agent_id": "agent-...", "conversation_id": "conv-..." }, "payload": { "kind": "approval_response", "request_id": "req-...", "decision": { "behavior": "allow", "message": "Approved" } }}Send deny
{ "type": "input", "runtime": { "agent_id": "agent-...", "conversation_id": "conv-..." }, "payload": { "kind": "approval_response", "request_id": "req-...", "decision": { "behavior": "deny", "message": "Do not run this command." } }}Receive
Streaming resumes with stream_delta and update_loop_status events.
abort_message
Section titled “abort_message”Cancel an active run.
Send
{ "type": "abort_message", "runtime": { "agent_id": "agent-...", "conversation_id": "conv-..." }, "request_id": "abort-...", "run_id": "run-..."}Receive
{ "type": "cancel_ack", "seq": 16, "request_id": "abort-...", "accepted": true, "run_id": "run-..."}list_models
Section titled “list_models”Fetch models available to the device.
Send
{ "type": "list_models", "request_id": "models-..."}Receive
{ "type": "list_models_response", "seq": 20, "request_id": "models-...", "success": true, "entries": [ { "id": "model-entry-id", "handle": "openai/gpt-4o-mini", "label": "GPT-4o mini", "description": "OpenAI GPT-4o mini", "isDefault": true, "isFeatured": true, "free": false } ], "available_handles": ["openai/gpt-4o-mini"]}update_model
Section titled “update_model”Set the active model for the runtime.
Send by handle
{ "type": "update_model", "request_id": "update-model-...", "runtime": { "agent_id": "agent-...", "conversation_id": "conv-..." }, "payload": { "model_handle": "openai/gpt-4o-mini" }}Send by model entry ID
{ "type": "update_model", "request_id": "update-model-...", "runtime": { "agent_id": "agent-...", "conversation_id": "conv-..." }, "payload": { "model_id": "model-entry-id" }}Receive
{ "type": "update_model_response", "seq": 21, "request_id": "update-model-...", "success": true, "runtime": { "agent_id": "agent-...", "conversation_id": "conv-..." }, "applied_to": "conversation", "model_handle": "openai/gpt-4o-mini", "model_settings": { "max_tokens": 4096 }}change_device_state
Section titled “change_device_state”Change runtime/device state.
Send permission mode
{ "type": "change_device_state", "runtime": { "agent_id": "agent-...", "conversation_id": "conv-..." }, "payload": { "mode": "acceptEdits" }}Send current working directory
{ "type": "change_device_state", "runtime": { "agent_id": "agent-...", "conversation_id": "conv-..." }, "payload": { "cwd": "/workspace/project" }}Send git operation
{ "type": "change_device_state", "runtime": { "agent_id": "agent-...", "conversation_id": "conv-..." }, "payload": { "git_op": { "kind": "checkout", "branch": "feature/foo" } }}Receive
update_device_statuscwd_changedfor some legacy CWD flows
search_branches
Section titled “search_branches”Search local git branches on the device.
Send
{ "type": "search_branches", "request_id": "branches-...", "query": "feature"}Receive
{ "type": "search_branches_response", "request_id": "branches-...", "success": true, "branches": ["feature/foo"]}checkout_branch
Section titled “checkout_branch”Checkout or create a branch on the device.
Send checkout
{ "type": "checkout_branch", "request_id": "checkout-...", "branch": "feature/foo"}Send create and checkout
{ "type": "checkout_branch", "request_id": "checkout-...", "branch": "feature/new-branch", "create": true}Receive
{ "type": "checkout_branch_response", "request_id": "checkout-...", "success": true, "branch": "feature/foo"}File commands
Section titled “File commands”Use these for direct filesystem operations on the device.
| Command | Response |
|---|---|
list_in_directory | list_in_directory_response |
read_file | read_file_response |
write_file | write_file_response |
edit_file | edit_file_response |
Each request should include request_id. Match the response by the same request_id.
Terminal commands
Section titled “Terminal commands”Use these for PTY control on the device.
| Command | Purpose |
|---|---|
terminal_spawn | Create a terminal session. |
terminal_input | Write input to a terminal session. |
terminal_resize | Resize a terminal session. |
terminal_kill | Stop a terminal session. |
Events
Section titled “Events”update_device_status
Section titled “update_device_status”Device state snapshot.
{ "type": "update_device_status", "seq": 1, "runtime": { "agent_id": "agent-...", "conversation_id": "conv-..." }, "event_seq": 10, "idempotency_key": "device-event-10", "device_status": { "is_online": true, "is_processing": false, "current_permission_mode": "standard", "current_working_directory": "/workspace/project", "git_context": { "branch": "main", "recent_branches": ["feature/foo", "fix/bar"] }, "memory_directory": "~/.letta/agents/.../memory", "letta_code_version": "0.27.7", "supported_commands": ["input", "sync", "change_device_state"] }}update_loop_status
Section titled “update_loop_status”Execution lifecycle state.
{ "type": "update_loop_status", "runtime": { "agent_id": "agent-...", "conversation_id": "conv-..." }, "event_seq": 11, "idempotency_key": "device-event-11", "loop_status": { "status": "PROCESSING_API_RESPONSE", "active_run_ids": ["run-..."] }}Common statuses:
SENDING_API_REQUESTWAITING_FOR_API_RESPONSERETRYING_API_REQUESTPROCESSING_API_RESPONSEEXECUTING_CLIENT_SIDE_TOOLEXECUTING_COMMANDWAITING_ON_APPROVALWAITING_ON_INPUT
stream_delta
Section titled “stream_delta”Incremental assistant/tool/reasoning content.
{ "type": "stream_delta", "runtime": { "agent_id": "agent-...", "conversation_id": "conv-..." }, "event_seq": 12, "idempotency_key": "device-event-12", "delta": { "message_type": "assistant_message", "content": "The WebSocket protocol lives in ..." }}control_request
Section titled “control_request”Tool approval request.
{ "type": "control_request", "seq": 14, "request_id": "req-...", "request": { "subtype": "can_use_tool", "tool_name": "Bash", "tool_call_id": "toolu_...", "input": { "cmd": "npm test" } }}Other events
Section titled “Other events”Status/control events:
| Event | Meaning |
|---|---|
pong | Heartbeat response. |
cancel_ack | Abort acknowledgement. |
cwd_changed | Legacy CWD change acknowledgement. |
error | Generic runtime or routing error. |
run_request_error | Error starting or running a turn. |
status | Legacy online/mode/heartbeat status frame. |
state_response | Legacy full state snapshot. |
run_started | Legacy run started event. |
run_completed | Legacy run completed event. |
mode_changed | Legacy permission-mode change acknowledgement. |
message | Legacy raw message frame. |
Queue events:
| Event | Meaning |
|---|---|
update_queue | Protocol V2 queue state snapshot. |
queue_item_enqueued | Legacy queue item added. |
queue_batch_dequeued | Legacy queue batch removed for execution. |
queue_blocked | Legacy queue blocked event. |
queue_cleared | Legacy queue cleared event. |
queue_item_dropped | Legacy queue item dropped event. |
Filesystem events and responses:
| Event | Meaning |
|---|---|
list_in_directory_response | Response to list_in_directory. |
get_tree_response | Response to get_tree. |
read_file_response | Response to read_file. |
write_file_response | Response to write_file. |
edit_file_response | Response to edit_file. |
file_ops | File operation patch/update event. |
file_changed | Watched file changed event. |
search_files_response | Response to search_files. |
grep_in_files_response | Response to grep_in_files. |
Memory and MemFS events and responses:
| Event | Meaning |
|---|---|
list_memory_response | Response to list_memory. |
memory_history_response | Response to memory_history. |
memory_file_at_ref_response | Response to memory_file_at_ref. |
memory_commit_diff_response | Response to memory_commit_diff. |
read_memory_file_response | Response to read_memory_file. |
write_memory_file_response | Response to write_memory_file. |
delete_memory_file_response | Response to delete_memory_file. |
memory_updated | Memory files changed on disk. |
enable_memfs_response | Response to enable_memfs. |
Model and provider responses:
| Event | Meaning |
|---|---|
list_models_response | Response to list_models. |
update_model_response | Response to update_model. |
list_connect_providers_response | Response to list_connect_providers. |
connect_provider_response | Response to connect_provider. |
disconnect_provider_response | Response to disconnect_provider. |
Terminal events:
| Event | Meaning |
|---|---|
terminal_spawned | Terminal session created. |
terminal_output | Terminal output bytes. |
terminal_exited | Terminal session exited. |
Git/search/experiment/secrets responses:
| Event | Meaning |
|---|---|
search_branches_response | Response to search_branches. |
checkout_branch_response | Response to checkout_branch. |
get_experiments_response | Response to get_experiments. |
set_experiment_response | Response to set_experiment. |
secret_list_response | Response to secret_list. |
secret_apply_response | Response to secret_apply. |
Skill and legacy approval responses:
| Event | Meaning |
|---|---|
skill_enable_response | Response to skill_enable. |
skill_disable_response | Response to skill_disable. |
recover_pending_approvals_ack | Acknowledgement for recover_pending_approvals. |
Channels responses and push events:
| Event | Meaning |
|---|---|
channels_list_response | Response to channels_list. |
channel_accounts_list_response | Response to channel_accounts_list. |
channel_account_create_response | Response to channel_account_create. |
channel_account_update_response | Response to channel_account_update. |
channel_account_bind_response | Response to channel_account_bind. |
channel_account_unbind_response | Response to channel_account_unbind. |
channel_account_delete_response | Response to channel_account_delete. |
channel_account_start_response | Response to channel_account_start. |
channel_account_stop_response | Response to channel_account_stop. |
channel_get_config_response | Response to channel_get_config. |
channel_set_config_response | Response to channel_set_config. |
channel_start_response | Response to channel_start. |
channel_stop_response | Response to channel_stop. |
channel_pairings_list_response | Response to channel_pairings_list. |
channel_pairing_bind_response | Response to channel_pairing_bind. |
channel_routes_list_response | Response to channel_routes_list. |
channel_targets_list_response | Response to channel_targets_list. |
channel_target_bind_response | Response to channel_target_bind. |
channel_route_remove_response | Response to channel_route_remove. |
channels_updated | Channels changed. |
channel_accounts_updated | Channel accounts changed. |
channel_pairings_updated | Channel pairings changed. |
channel_routes_updated | Channel routes changed. |
channel_targets_updated | Channel targets changed. |
Reliability
Section titled “Reliability”- Send
pingevery 30 seconds. - Send
ackfor every message with numericseq. - Track
event_seq; sendsyncif you detect a gap. - Deduplicate events with
idempotency_keywhen present. - Send
syncwithrecover_approvals: trueafter initial attach or reconnect. - Treat
WAITING_ON_INPUTas idle.