Skip to main content
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.
import { Sandbox } from 'e2b'

const sandbox = await Sandbox.create()

const terminal = await sandbox.pty.create({
  cols: 80,              // Terminal width in characters
  rows: 24,              // Terminal height in characters
  onData: (data) => {
    // Called whenever terminal outputs data
    process.stdout.write(data)
  },
  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
console.log('Terminal PID:', terminal.pid)
from e2b import Sandbox

sandbox = Sandbox()

terminal = sandbox.pty.create(
    cols=80,              # Terminal width in characters
    rows=24,              # Terminal height in characters
    on_data=lambda data: print(data.decode(), end=''),  # end='' prevents print from adding extra newline
    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)
The PTY runs an interactive bash shell with TERM=xterm-256color, which supports ANSI colors and escape sequences.

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.
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
})
from e2b import Sandbox

sandbox = Sandbox()

# end='' prevents print() from adding an extra newline
# (PTY output already contains newlines)
terminal = sandbox.pty.create(
    cols=80,
    rows=24,
    on_data=lambda data: print(data.decode(), end=''),
    timeout=0,  # Keep the session open indefinitely
)

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 will be delivered to your onData callback.
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')
)
from e2b import Sandbox

sandbox = Sandbox()

terminal = sandbox.pty.create(
    cols=80,
    rows=24,
    on_data=lambda data: print(data.decode(), end=''),  # end='' prevents extra newline
)

# 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')

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.
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,
})
from e2b import Sandbox

sandbox = Sandbox()

terminal = sandbox.pty.create(
    cols=80,
    rows=24,
    on_data=lambda data: print(data.decode(), end=''),  # end='' prevents extra newline
)

# Resize to new dimensions (in characters)
sandbox.pty.resize(terminal.pid, cols=120, rows=40)

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
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()
import time
from e2b import Sandbox

sandbox = Sandbox()

# Create a PTY session
terminal = sandbox.pty.create(
    cols=80,
    rows=24,
    on_data=lambda data: print('Handler 1:', data.decode()),
)

pid = terminal.pid

# Send a command
sandbox.pty.send_stdin(pid, b'echo hello\n')
time.sleep(0.5)

# Disconnect - PTY keeps running in the background
terminal.disconnect()

# Later: reconnect with a new data handler
reconnected = sandbox.pty.connect(
    pid,
    on_data=lambda data: print('Handler 2:', data.decode()),
)

# Continue using the session
sandbox.pty.send_stdin(pid, b'echo world\n')

# Wait for the terminal to exit
reconnected.wait()

Kill the PTY

Terminate the PTY session with kill().
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()
from e2b import Sandbox

sandbox = Sandbox()

terminal = sandbox.pty.create(
    cols=80,
    rows=24,
    on_data=lambda data: print(data.decode(), end=''),  # end='' prevents extra newline
)

# Kill the PTY
killed = sandbox.pty.kill(terminal.pid)
print('Killed:', killed)  # True if successful

# Or use the handle method
# terminal.kill()

Wait for PTY to exit

Use wait() to wait for the terminal session to end (e.g., when the user types exit).
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)
from e2b import Sandbox

sandbox = Sandbox()

terminal = sandbox.pty.create(
    cols=80,
    rows=24,
    on_data=lambda data: print(data.decode(), end=''),  # end='' prevents extra newline
)

# Send exit command
sandbox.pty.send_stdin(terminal.pid, b'exit\n')

# Wait for the terminal to exit
result = terminal.wait()
print('Exit code:', result.exit_code)

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 which uses the same sandbox.pty API documented above.