Skip to main content

Execute & Batch

Send a prompt (or a raw command) into one terminal — or fan the same prompt out to many terminals at once — over the local REST API. These are the endpoints an orchestrator uses to "type" into a terminal on behalf of an agent.

Base URL: http://127.0.0.1:42031/api

Auth

In the default localhost-only mode every endpoint is unauthenticated — it is safe because the server is bound to 127.0.0.1. A bearer token is required only when you turn on Expose on local network in Settings → Connections. See Overview & auth for the full model.

What "execute" actually does

An execute call does two things to the target terminal's PTY, in order:

  1. Writes your prompt as a bracketed paste (wrapped in CSI 200~ … 201~), so embedded newlines are inserted as literal multi-line input rather than submitting early.
  2. Writes a submit sequence — the keystrokes that make the target program act on what was just pasted.

The submit sequence is chosen from cliType (e.g. Copilot vs Claude vs a plain shell) unless you override it with submissionSignal or customPattern. This is why execute is aimed at driving interactive AI CLIs that need a specific key combination to submit a prompt — not just running shell commands.

An honest note: execute is fire-and-forget. The call writes the keystrokes and returns immediately — it does not wait for the command or agent to finish, and it does not return the terminal's output. To read what happened, poll GET /api/terminals/:id/output or subscribe to the WebSocket output stream.

Execute a prompt in one terminal

POST /api/terminals/:id/execute
POST /api/terminals/:id/prompt (alias — identical behavior)

Request body

FieldTypeRequiredDefaultNotes
promptstringYesMust be non-empty. Sent as a bracketed paste; may contain newlines.
cliTypestringNocopilotSelects the submit sequence. See the table below.
submissionSignalstringNoExact keystroke sequence to send after the prompt. Highest priority — overrides cliType and customPattern.
customPatternobjectNoUsed only when cliType is custom. Shape: { "separator"?: string, "end_indicator": string }.

cliType values

ValueIntended target
copilotGitHub Copilot CLI (the default)
claudeClaude Code / Claude CLI
geminiGemini CLI
chatgptChatGPT-style CLI
defaultA plain shell command (submits with a normal Enter)
customUse the keystrokes in customPattern

An unrecognized cliType (that is not custom and has no built-in pattern) returns 400 Unknown CLI type: <value>.

Example — send a prompt to a Claude terminal

curl -X POST http://127.0.0.1:42031/api/terminals/term-abc123/execute \
-H "Content-Type: application/json" \
-d '{ "prompt": "Refactor utils.ts and run the tests", "cliType": "claude" }'

Success returns 200 with:

{
"success": true,
"prompt": "Refactor utils.ts and run the tests",
"cliType": "claude"
}

Errors (single execute)

StatusBodyWhen
400{ "error": "Prompt must be a non-empty string" }prompt missing or empty
400{ "error": "Unknown CLI type: <value>" }Unrecognized cliType
404{ "error": "Terminal not found" }No terminal with that id
note

Unlike the raw /input, /size, and /resize endpoints — which return 200 with an { "error": ... } body for a missing terminal — single execute returns a real 404. Check the status code and the body.

Submission signals

submissionSignal lets you specify the exact keystrokes sent after the prompt, which is essential for interactive CLIs that submit on an unusual combination. Use standard JSON string escaping for control characters.

{ "prompt": "ls -la", "submissionSignal": "\r\n" }
SignalMeaningTypical use
\rCarriage returnStandard macOS/Linux shell submit
\r\nCRLFStandard Windows shell submit
\r\rDouble returnAI CLIs that submit on an empty line
\u001b\rEscape + EnterClaude CLI submission
\r\n\r\nDouble CRLFRobust submit for Node-based CLIs
\u0003Ctrl+CSend an interrupt
\u0004Ctrl+DSend EOF

Precedence: submissionSignal wins over everything. If it is present, cliType and customPattern are ignored. When absent and cliType is custom, customPattern.end_indicator (optionally preceded by separator) is used; otherwise the built-in cliType sequence applies.

Fan out to many terminals

Send the same prompt to a list of terminals in one call. Ids are de-duplicated (first-seen order preserved), so one terminal is never written twice.

POST /api/terminals/batch/execute

Request body

FieldTypeRequiredDefaultNotes
terminalIdsstring[]YesNon-empty list of terminal ids.
promptstringYesNon-empty.
cliTypestringNocopilotSame values as single execute.
submissionSignalstringNoSame semantics as single execute.
customPatternobjectNoSame shape as single execute.

The submit pattern is validated once up front. An empty terminalIds, an empty prompt, an unknown cliType, or a missing customPattern (when cliType is custom) fails the whole batch with 400 — because the pattern is identical for every id, a malformed pattern is a bad request, not a per-terminal failure.

Example

curl -X POST http://127.0.0.1:42031/api/terminals/batch/execute \
-H "Content-Type: application/json" \
-d '{
"terminalIds": ["term-a", "term-b", "term-c"],
"prompt": "git pull && bun install",
"cliType": "default"
}'

Response

The batch always returns 200 with a per-terminal results array plus a summary. A bad id (terminal not found) becomes a per-terminal failure — it never blocks the others:

{
"results": [
{ "terminalId": "term-a", "success": true },
{ "terminalId": "term-b", "success": true },
{ "terminalId": "term-c", "success": false, "error": "Terminal not found" }
],
"summary": { "total": 3, "succeeded": 2, "failed": 1 }
}

Fan out raw input

Write a raw string to many terminals — no prompt wrapping, no submit sequence appended. This is the batch equivalent of the single-terminal /input endpoint: what you send is exactly what the PTYs receive.

POST /api/terminals/batch/input

Request body

FieldTypeRequiredNotes
terminalIdsstring[]YesNon-empty; de-duplicated in order.
datastringYesRaw bytes to write. Include your own \r/\n if you want submission.

Example — interrupt two terminals at once (Ctrl+C)

curl -X POST http://127.0.0.1:42031/api/terminals/batch/input \
-H "Content-Type: application/json" \
-d '{ "terminalIds": ["term-a", "term-b"], "data": "\u0003" }'

Returns the same { results, summary } shape as batch execute, always with 200.

Fan-out flow

Equivalent MCP tool

Agents connected over MCP get the same capability through the execute_command tool — passing an array of terminal ids fans the command out just like /batch/execute. See MCP tools.

Next steps