> ## Documentation Index
> Fetch the complete documentation index at: https://e2b.manicule.dev/llms.txt
> Use this file to discover all available pages before exploring further.
> For every page in one file, fetch https://e2b.manicule.dev/llms-full.txt (large).
> To search and read these docs as tools, connect the MCP server at https://e2b.manicule.dev/api/mcp.
> Every agent surface: https://e2b.manicule.dev/.well-known/llms-manifest.json

# Interactive terminal (PTY) (/docs/sandbox/pty)

<!-- agent-signals: reading_time_min: 6 · est_tokens: 2513 · updated: 2026-07-30 -->
Related: [Auto-resume on request](/docs/sandbox/auto-resume.md), [Connect to running sandbox](/docs/sandbox/connect.md), [Environment variables](/docs/sandbox/environment-variables.md), [Filesystem-only snapshots](/docs/sandbox/filesystem-only-snapshots.md), [Sandbox forking](/docs/sandbox/fork.md), [Git integration](/docs/sandbox/git-integration.md)

The PTY (pseudo-terminal) module allows you to create interactive terminal sessions in the sandbox with real-time, bidirectional communication.

Unlike `commands.run()` which executes a command and returns output after completion, PTY provides:

* **Real-time streaming** - Output is streamed as it happens via callbacks
* **Bidirectional input** - Send input while the terminal is running
* **Interactive shell** - Full terminal support with ANSI colors and escape sequences
* **Session persistence** - Disconnect and reconnect to running sessions

## Create a PTY session

Use `sandbox.pty.create()` to start an interactive bash shell. This example runs `echo 'hello world'`, then exits and prints the terminal output.

<CodeGroup>
```js title="JavaScript & TypeScript"
import { Sandbox } from 'e2b'

const sandbox = await Sandbox.create()

const terminal = await sandbox.pty.create({
  cols: 80,                    // Terminal size in characters
  rows: 24,
  envs: { MY_VAR: 'hello' },   // Optional environment variables
  cwd: '/home/user',           // Optional working directory
  user: 'root',                // Optional user to run as
  onData: (data) => {
    // Called whenever the terminal outputs data
    process.stdout.write(data)
  },
})

// terminal.pid contains the process ID
console.log('Terminal PID:', terminal.pid)

// Send input to the PTY. Data must be bytes (Uint8Array), and the trailing newline "presses Enter".
await sandbox.pty.sendInput(terminal.pid, new TextEncoder().encode("echo 'hello world'\n"))

// The PTY runs an interactive login shell that won't exit on its own, so tell it
// to exit — otherwise wait() below blocks forever.
await sandbox.pty.sendInput(terminal.pid, new TextEncoder().encode('exit\n'))

// Wait for the terminal to exit (output is streamed to onData above)
await terminal.wait()
```

```python title="Python"
from e2b import Sandbox, PtySize

sandbox = Sandbox.create()

terminal = sandbox.pty.create(
    size=PtySize(rows=24, cols=80),  # Terminal size in characters
    envs={'MY_VAR': 'hello'},        # Optional environment variables
    cwd='/home/user',                # Optional working directory
    user='root',                     # Optional user to run as
)

# terminal.pid contains the process ID
print('Terminal PID:', terminal.pid)

# Send input to the PTY via the pty module. Data must be bytes, and the trailing newline "presses Enter".
sandbox.pty.send_stdin(terminal.pid, b"echo 'hello world'\n")

# The PTY runs an interactive login shell that won't exit on its own, so tell it
# to exit — otherwise wait() below blocks forever.
sandbox.pty.send_stdin(terminal.pid, b"exit\n")

# Stream output by passing on_pty to wait()
# end='' prevents print from adding an extra newline
terminal.wait(on_pty=lambda data: print(data.decode(), end=''))
```
</CodeGroup>

<Note>The PTY runs an interactive bash shell with `TERM=xterm-256color`, which supports ANSI colors and escape sequences.</Note>

## Timeout

PTY sessions have a configurable timeout that controls the session duration. The default is 60 seconds. For interactive or long-running sessions, set `timeoutMs: 0` (JavaScript) or `timeout=0` (Python) to keep the session open indefinitely.

<CodeGroup>
```js title="JavaScript & TypeScript"
import { Sandbox } from 'e2b'

const sandbox = await Sandbox.create()

const terminal = await sandbox.pty.create({
  cols: 80,
  rows: 24,
  onData: (data) => process.stdout.write(data),
  timeoutMs: 0,  // Keep the session open indefinitely
})
```

```python title="Python"
from e2b import Sandbox, PtySize

sandbox = Sandbox.create()

terminal = sandbox.pty.create(
    PtySize(rows=24, cols=80),
    timeout=0,  # Keep the session open indefinitely
)

# end='' prevents print() from adding an extra newline
# (PTY output already contains newlines)
terminal.wait(on_pty=lambda data: print(data.decode(), end=''))
```
</CodeGroup>

## Send input to PTY

Use `sendInput()` in JavaScript or `send_stdin()` in Python to send data to the terminal. These methods return a Promise (JavaScript) or complete synchronously (Python) - the actual output is delivered to your `onData` callback (JavaScript) or to the `on_pty` callback you pass to `wait()` (Python).

<CodeGroup>
```js title="JavaScript & TypeScript"
import { Sandbox } from 'e2b'

const sandbox = await Sandbox.create()

const terminal = await sandbox.pty.create({
  cols: 80,
  rows: 24,
  onData: (data) => process.stdout.write(data),
})

// Send a command (don't forget the newline!)
await sandbox.pty.sendInput(
  terminal.pid,
  new TextEncoder().encode('echo "Hello from PTY"\n')
)
```

```python title="Python"
from e2b import Sandbox, PtySize

sandbox = Sandbox.create()

terminal = sandbox.pty.create(PtySize(rows=24, cols=80))

# Send a command as bytes (b'...' is Python's byte string syntax)
# Don't forget the newline!
sandbox.pty.send_stdin(terminal.pid, b'echo "Hello from PTY"\n')

# Receive output by passing on_pty to wait()
# end='' prevents print from adding an extra newline
terminal.wait(on_pty=lambda data: print(data.decode(), end=''))
```
</CodeGroup>

## Resize the terminal

When the user's terminal window changes size, notify the PTY with `resize()`. The `cols` and `rows` parameters are measured in characters, not pixels.

<CodeGroup>
```js title="JavaScript & TypeScript"
import { Sandbox } from 'e2b'

const sandbox = await Sandbox.create()

const terminal = await sandbox.pty.create({
  cols: 80,
  rows: 24,
  onData: (data) => process.stdout.write(data),
})

// Resize to new dimensions (in characters)
await sandbox.pty.resize(terminal.pid, {
  cols: 120,
  rows: 40,
})
```

```python title="Python"
from e2b import Sandbox, PtySize

sandbox = Sandbox.create()

terminal = sandbox.pty.create(PtySize(rows=24, cols=80))

# Resize to new dimensions (in characters)
sandbox.pty.resize(terminal.pid, PtySize(rows=40, cols=120))
```
</CodeGroup>

## Disconnect and reconnect

You can disconnect from a PTY session while keeping it running, then reconnect later with a new data handler. This is useful for:

* Resuming terminal sessions after network interruptions
* Sharing terminal access between multiple clients
* Implementing terminal session persistence

<CodeGroup>
```js title="JavaScript & TypeScript"
import { Sandbox } from 'e2b'

const sandbox = await Sandbox.create()

// Create a PTY session
const terminal = await sandbox.pty.create({
  cols: 80,
  rows: 24,
  onData: (data) => console.log('Handler 1:', new TextDecoder().decode(data)),
})

const pid = terminal.pid

// Send a command
await sandbox.pty.sendInput(pid, new TextEncoder().encode('echo hello\n'))

// Disconnect - PTY keeps running in the background
await terminal.disconnect()

// Later: reconnect with a new data handler
const reconnected = await sandbox.pty.connect(pid, {
  onData: (data) => console.log('Handler 2:', new TextDecoder().decode(data)),
})

// Continue using the session
await sandbox.pty.sendInput(pid, new TextEncoder().encode('echo world\n'))

// Wait for the terminal to exit
await reconnected.wait()
```

```python title="Python"
from e2b import Sandbox, PtySize

sandbox = Sandbox.create()

# Create a PTY session
terminal = sandbox.pty.create(PtySize(rows=24, cols=80))

pid = terminal.pid

# Send a command
sandbox.pty.send_stdin(pid, b'echo hello\n')

# Disconnect - PTY keeps running in the background
terminal.disconnect()

# Later: reconnect to the same session
reconnected = sandbox.pty.connect(pid)

# Continue using the session, then exit
sandbox.pty.send_stdin(pid, b'echo world\nexit\n')

# Wait for the terminal to exit, streaming output via on_pty
reconnected.wait(on_pty=lambda data: print('Handler:', data.decode()))
```
</CodeGroup>

## Kill the PTY

Terminate the PTY session with `kill()`.

<CodeGroup>
```js title="JavaScript & TypeScript"
import { Sandbox } from 'e2b'

const sandbox = await Sandbox.create()

const terminal = await sandbox.pty.create({
  cols: 80,
  rows: 24,
  onData: (data) => process.stdout.write(data),
})

// Kill the PTY
const killed = await sandbox.pty.kill(terminal.pid)
console.log('Killed:', killed)  // true if successful

// Or use the handle method
// await terminal.kill()
```

```python title="Python"
from e2b import Sandbox, PtySize

sandbox = Sandbox.create()

terminal = sandbox.pty.create(PtySize(rows=24, cols=80))

# Kill the PTY
killed = sandbox.pty.kill(terminal.pid)
print('Killed:', killed)  # True if successful

# Or use the handle method
# terminal.kill()
```
</CodeGroup>

## Wait for PTY to exit

Use `wait()` to wait for the terminal session to end (e.g., when the user types `exit`).

<CodeGroup>
```js title="JavaScript & TypeScript"
import { Sandbox } from 'e2b'

const sandbox = await Sandbox.create()

const terminal = await sandbox.pty.create({
  cols: 80,
  rows: 24,
  onData: (data) => process.stdout.write(data),
})

// Send exit command
await sandbox.pty.sendInput(terminal.pid, new TextEncoder().encode('exit\n'))

// Wait for the terminal to exit
const result = await terminal.wait()
console.log('Exit code:', result.exitCode)
```

```python title="Python"
from e2b import Sandbox, PtySize

sandbox = Sandbox.create()

terminal = sandbox.pty.create(PtySize(rows=24, cols=80))

# Send exit command
sandbox.pty.send_stdin(terminal.pid, b'exit\n')

# Wait for the terminal to exit, streaming output via on_pty
result = terminal.wait(on_pty=lambda data: print(data.decode(), end=''))
print('Exit code:', result.exit_code)
```
</CodeGroup>

## Interactive terminal (SSH-like)

Building a fully interactive terminal (like SSH) requires handling raw mode, stdin forwarding, and terminal resize events. For a production implementation, see the [E2B CLI source code](https://github.com/e2b-dev/E2B/blob/main/packages/cli/src/terminal.ts) which uses the same `sandbox.pty` API documented above.
