Skip to content
Letta Code Letta Code Letta Docs
Sign up
Remote Client API

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.

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>

Use this to find an online Letta Code environment and get its connectionId.

Request

GET /v1/environments?onlineOnly=true
Authorization: Bearer $LETTA_API_KEY

Response

{
"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.

Connect to the status WebSocket:

wss://api.letta.com/v1/environments/{connectionId}/status/ws?agentId={agentId}&conversationId={conversationId}&channel=stream

Browser fallback with query-token auth:

wss://api.letta.com/v1/environments/{connectionId}/status/ws?agentId={agentId}&conversationId={conversationId}&channel=stream&token={token}
FieldDirectionMeaning
typeBothMessage type. Commands and events are distinguished by type.
request_idClient → device, device → clientClient-generated ID for request/response matching.
runtime.agent_idClient → devicePersistent Letta agent ID.
runtime.conversation_idClient → deviceConversation/thread ID.
seqServer → clientTransport sequence number. ACK with { "type": "ack", "seq": n }.
event_seqDevice → clientDevice event sequence number. Use to detect missed events.
idempotency_keyDevice → clientEvent dedupe key.

Most one-shot commands return a response event with the same request_id. Match responses to requests by request_id.

Filesystem responses:

Send commandReceive event
list_in_directorylist_in_directory_response
get_treeget_tree_response
read_fileread_file_response
write_filewrite_file_response
edit_fileedit_file_response
search_filessearch_files_response
grep_in_filesgrep_in_files_response

Memory and MemFS responses:

Send commandReceive event
list_memorylist_memory_response
memory_historymemory_history_response
memory_file_at_refmemory_file_at_ref_response
memory_commit_diffmemory_commit_diff_response
read_memory_fileread_memory_file_response
write_memory_filewrite_memory_file_response
delete_memory_filedelete_memory_file_response
enable_memfsenable_memfs_response

Model and provider responses:

Send commandReceive event
list_modelslist_models_response
update_modelupdate_model_response
list_connect_providerslist_connect_providers_response
connect_providerconnect_provider_response
disconnect_providerdisconnect_provider_response

Git, experiments, secrets, and skills responses:

Send commandReceive event
search_branchessearch_branches_response
checkout_branchcheckout_branch_response
get_experimentsget_experiments_response
set_experimentset_experiment_response
secret_listsecret_list_response
secret_applysecret_apply_response
skill_enableskill_enable_response
skill_disableskill_disable_response

Channels responses:

Send commandReceive event
channels_listchannels_list_response
channel_accounts_listchannel_accounts_list_response
channel_account_createchannel_account_create_response
channel_account_updatechannel_account_update_response
channel_account_bindchannel_account_bind_response
channel_account_unbindchannel_account_unbind_response
channel_account_deletechannel_account_delete_response
channel_account_startchannel_account_start_response
channel_account_stopchannel_account_stop_response
channel_get_configchannel_get_config_response
channel_set_configchannel_set_config_response
channel_startchannel_start_response
channel_stopchannel_stop_response
channel_pairings_listchannel_pairings_list_response
channel_pairing_bindchannel_pairing_bind_response
channel_routes_listchannel_routes_list_response
channel_targets_listchannel_targets_list_response
channel_target_bindchannel_target_bind_response
channel_route_removechannel_route_remove_response

Other acknowledgements and non-_response replies:

Send commandReceive event
pingpong
abort_messagecancel_ack
change_cwdcwd_changed
recover_pending_approvalsrecover_pending_approvals_ack
syncState events such as update_device_status, update_loop_status, and update_queue
input with create_messageStreaming events such as update_loop_status, stream_delta, and control_request
change_device_stateState events such as update_device_status

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_status
  • update_loop_status
  • update_queue
  • control_request or pending approvals, when recover_approvals is true

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_status
  • stream_delta
  • control_request
  • update_queue
  • error or run_request_error

The turn is idle when update_loop_status.loop_status.status is WAITING_ON_INPUT.

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.

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-..."
}

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"]
}

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 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_status
  • cwd_changed for some legacy CWD flows

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 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"
}

Use these for direct filesystem operations on the device.

CommandResponse
list_in_directorylist_in_directory_response
read_fileread_file_response
write_filewrite_file_response
edit_fileedit_file_response

Each request should include request_id. Match the response by the same request_id.

Use these for PTY control on the device.

CommandPurpose
terminal_spawnCreate a terminal session.
terminal_inputWrite input to a terminal session.
terminal_resizeResize a terminal session.
terminal_killStop a terminal session.

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"]
}
}

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_REQUEST
  • WAITING_FOR_API_RESPONSE
  • RETRYING_API_REQUEST
  • PROCESSING_API_RESPONSE
  • EXECUTING_CLIENT_SIDE_TOOL
  • EXECUTING_COMMAND
  • WAITING_ON_APPROVAL
  • WAITING_ON_INPUT

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 ..."
}
}

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"
}
}
}

Status/control events:

EventMeaning
pongHeartbeat response.
cancel_ackAbort acknowledgement.
cwd_changedLegacy CWD change acknowledgement.
errorGeneric runtime or routing error.
run_request_errorError starting or running a turn.
statusLegacy online/mode/heartbeat status frame.
state_responseLegacy full state snapshot.
run_startedLegacy run started event.
run_completedLegacy run completed event.
mode_changedLegacy permission-mode change acknowledgement.
messageLegacy raw message frame.

Queue events:

EventMeaning
update_queueProtocol V2 queue state snapshot.
queue_item_enqueuedLegacy queue item added.
queue_batch_dequeuedLegacy queue batch removed for execution.
queue_blockedLegacy queue blocked event.
queue_clearedLegacy queue cleared event.
queue_item_droppedLegacy queue item dropped event.

Filesystem events and responses:

EventMeaning
list_in_directory_responseResponse to list_in_directory.
get_tree_responseResponse to get_tree.
read_file_responseResponse to read_file.
write_file_responseResponse to write_file.
edit_file_responseResponse to edit_file.
file_opsFile operation patch/update event.
file_changedWatched file changed event.
search_files_responseResponse to search_files.
grep_in_files_responseResponse to grep_in_files.

Memory and MemFS events and responses:

EventMeaning
list_memory_responseResponse to list_memory.
memory_history_responseResponse to memory_history.
memory_file_at_ref_responseResponse to memory_file_at_ref.
memory_commit_diff_responseResponse to memory_commit_diff.
read_memory_file_responseResponse to read_memory_file.
write_memory_file_responseResponse to write_memory_file.
delete_memory_file_responseResponse to delete_memory_file.
memory_updatedMemory files changed on disk.
enable_memfs_responseResponse to enable_memfs.

Model and provider responses:

EventMeaning
list_models_responseResponse to list_models.
update_model_responseResponse to update_model.
list_connect_providers_responseResponse to list_connect_providers.
connect_provider_responseResponse to connect_provider.
disconnect_provider_responseResponse to disconnect_provider.

Terminal events:

EventMeaning
terminal_spawnedTerminal session created.
terminal_outputTerminal output bytes.
terminal_exitedTerminal session exited.

Git/search/experiment/secrets responses:

EventMeaning
search_branches_responseResponse to search_branches.
checkout_branch_responseResponse to checkout_branch.
get_experiments_responseResponse to get_experiments.
set_experiment_responseResponse to set_experiment.
secret_list_responseResponse to secret_list.
secret_apply_responseResponse to secret_apply.

Skill and legacy approval responses:

EventMeaning
skill_enable_responseResponse to skill_enable.
skill_disable_responseResponse to skill_disable.
recover_pending_approvals_ackAcknowledgement for recover_pending_approvals.

Channels responses and push events:

EventMeaning
channels_list_responseResponse to channels_list.
channel_accounts_list_responseResponse to channel_accounts_list.
channel_account_create_responseResponse to channel_account_create.
channel_account_update_responseResponse to channel_account_update.
channel_account_bind_responseResponse to channel_account_bind.
channel_account_unbind_responseResponse to channel_account_unbind.
channel_account_delete_responseResponse to channel_account_delete.
channel_account_start_responseResponse to channel_account_start.
channel_account_stop_responseResponse to channel_account_stop.
channel_get_config_responseResponse to channel_get_config.
channel_set_config_responseResponse to channel_set_config.
channel_start_responseResponse to channel_start.
channel_stop_responseResponse to channel_stop.
channel_pairings_list_responseResponse to channel_pairings_list.
channel_pairing_bind_responseResponse to channel_pairing_bind.
channel_routes_list_responseResponse to channel_routes_list.
channel_targets_list_responseResponse to channel_targets_list.
channel_target_bind_responseResponse to channel_target_bind.
channel_route_remove_responseResponse to channel_route_remove.
channels_updatedChannels changed.
channel_accounts_updatedChannel accounts changed.
channel_pairings_updatedChannel pairings changed.
channel_routes_updatedChannel routes changed.
channel_targets_updatedChannel targets changed.
  • Send ping every 30 seconds.
  • Send ack for every message with numeric seq.
  • Track event_seq; send sync if you detect a gap.
  • Deduplicate events with idempotency_key when present.
  • Send sync with recover_approvals: true after initial attach or reconnect.
  • Treat WAITING_ON_INPUT as idle.