---
title: "Terminal API: create, type into and read sessions"
description: "Halo HTTP API for terminal sessions: create a session, send input, resize it, read back its output and close it again."
type: "reference"
audience: ["developer"]
lang: "en"
canonical: "https://hello-halo.cc/docs/en/developers/api/terminal.html"
last_updated: "2026-10-19T00:11:17+08:00"
source: "Halo 3.0.4 docs"
---
# API: Terminal

Halo HTTP API for terminal sessions: create a session, send input, resize it, read back its output and close it again.

These are the terminal sessions shown in Halo, which a user can watch and type into: create one, send input, resize it, read back its output and close it. Live output streams over the [WebSocket](https://hello-halo.cc/docs/en/developers/api/websocket.html#terminal). The terminal is available on macOS and Windows only; on Linux every terminal operation answers `403`.

Paths are relative to your Halo base URL, and every request carries the access password. Set `$HALO_URL` and `$HALO_TOKEN` as in the [quickstart](https://hello-halo.cc/docs/en/developers/quickstart.html); base URL, authentication and error conventions are in the [HTTP API overview](https://hello-halo.cc/docs/en/developers/api/).

| Operation | Endpoint |
| --- | --- |
| [Open a terminal session](#post-api-terminal-create) | `POST /api/terminal/create` |
| [Send keystrokes to a terminal session](#post-api-terminal-input) | `POST /api/terminal/input` |
| [Close a terminal session](#post-api-terminal-kill) | `POST /api/terminal/kill` |
| [List open terminal sessions](#get-api-terminal-list) | `GET /api/terminal/list` |
| [Read back a terminal session buffer](#post-api-terminal-replay) | `POST /api/terminal/replay` |
| [Resize a terminal session](#post-api-terminal-resize) | `POST /api/terminal/resize` |

## Open a terminal session {#post-api-terminal-create}

`POST /api/terminal/create`

- Opens a session with the platform's default shell in the workspace's working folder. Optional body fields: `cwd` (start folder) and `title`. The shell itself cannot be chosen.
- Live output arrives over the WebSocket as `terminal:data` events.

**Request body**

```json
{
  "spaceId": "<spaceId>"
}
```

**Response**

```ts
{success:true,data:{id,title,shell,cwd,cols,rows,owner:"user",state:"running",…}} | {success:false,error}
```

**Example**

```bash
curl -sX POST "$HALO_URL/api/terminal/create" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"spaceId":"<spaceId>"}'
```

## Send keystrokes to a terminal session {#post-api-terminal-input}

`POST /api/terminal/input`

- Writes raw keystrokes to the session; `\r` is Enter. Returns as soon as the input is written, before the command has produced output. Read the output from `terminal:data` events or [`POST /api/terminal/replay`](#post-api-terminal-replay).
- The WebSocket message `terminal-input` does the same without an HTTP round trip per keystroke.

**Request body**

```json
{
  "sessionId": "<sessionId>",
  "data": "ls\r"
}
```

**Response**

```ts
{success:true} | {success:false,error:"No such terminal session",code:"TERMINAL_NOT_FOUND"}
```

**Example**

```bash
curl -sX POST "$HALO_URL/api/terminal/input" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sessionId":"<sessionId>","data":"ls\r"}'
```

## Close a terminal session {#post-api-terminal-kill}

`POST /api/terminal/kill`

- Ends the shell process; anything still running in it stops. `TERMINAL_NOT_FOUND` means the session is already gone.

**Request body**

```json
{
  "sessionId": "<sessionId>"
}
```

**Response**

```ts
{success:true} | {success:false,error:"No such terminal session",code:"TERMINAL_NOT_FOUND"}
```

**Example**

```bash
curl -sX POST "$HALO_URL/api/terminal/kill" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sessionId":"<sessionId>"}'
```

## List open terminal sessions {#get-api-terminal-list}

`GET /api/terminal/list`

- Lists the terminal sessions on this computer, including the ones Halo's assistant opened (`owner: "ai"`). `aiTouched` turns true once the assistant has used a session.

**Response**

```ts
{success:true,data:[{id,title,shell,cwd,cols,rows,owner:"ai"|"user",aiTouched,state:"running"|"exited",exitCode,lastActivityAt,createdAt,spaceId?}]}
```

**Example**

```bash
curl -s "$HALO_URL/api/terminal/list" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## Read back a terminal session buffer {#post-api-terminal-replay}

`POST /api/terminal/replay`

- Returns the session's info and its buffered output as raw terminal data, ANSI escape codes included, so a terminal view can redraw itself after connecting.

**Request body**

```json
{
  "sessionId": "<sessionId>"
}
```

**Response**

```ts
{success:true,data:{info:{id,title,…},data:"<buffered output>"}} | {success:false,error,code:"TERMINAL_NOT_FOUND"}
```

**Example**

```bash
curl -sX POST "$HALO_URL/api/terminal/replay" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sessionId":"<sessionId>"}'
```

## Resize a terminal session {#post-api-terminal-resize}

`POST /api/terminal/resize`

- `cols` and `rows` must be positive integers.

**Request body**

```json
{
  "sessionId": "<sessionId>",
  "cols": 120,
  "rows": 32
}
```

**Response**

```ts
{success:true} | {success:false,error,code?:"TERMINAL_NOT_FOUND"}
```

**Example**

```bash
curl -sX POST "$HALO_URL/api/terminal/resize" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sessionId":"<sessionId>","cols":120,"rows":32}'
```
