> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wrenn.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Stream command output via WebSocket

> GET /v1/capsules/{id}/exec/stream — WebSocket endpoint for streaming command execution. Receive stdout/stderr events in real time as they arrive.

This endpoint upgrades an HTTP connection to a WebSocket so you can stream command output as it is produced. Unlike the blocking exec endpoint, output arrives event by event — useful for long-running processes, build jobs, or any command where you want to display progress in real time.

## Endpoint

```
GET https://app.wrenn.dev/api/v1/capsules/{id}/exec/stream
```

Upgrade this request to a WebSocket by including the standard `Upgrade: websocket` headers.

## Authentication

<ParamField header="X-API-Key" type="string" required>
  Your team's API key. Pass the full `wrn_...` key value.
</ParamField>

## Path parameters

<ParamField path="id" type="string" required>
  The capsule ID.
</ParamField>

## WebSocket protocol

Once the connection upgrades (HTTP 101), you control the session with JSON messages.

### Client messages

**Start a process** — send this as the first message after connecting:

```json theme={null}
{
  "type": "start",
  "cmd": "tail",
  "args": ["-f", "/var/log/app.log"]
}
```

| Field  | Type      | Description                       |
| ------ | --------- | --------------------------------- |
| `type` | string    | Must be `"start"`.                |
| `cmd`  | string    | Command to execute.               |
| `args` | string\[] | Arguments to pass to the command. |

**Stop the process** — send this to terminate the running process early:

```json theme={null}
{
  "type": "stop"
}
```

### Server messages

The server emits JSON messages as the process produces output:

| `type`   | Fields               | When sent                                                   |
| -------- | -------------------- | ----------------------------------------------------------- |
| `start`  | `pid: integer`       | Process started; contains the OS PID.                       |
| `stdout` | `data: string`       | A chunk of stdout arrived.                                  |
| `stderr` | `data: string`       | A chunk of stderr arrived.                                  |
| `exit`   | `exit_code: integer` | Process exited; connection closes automatically after this. |
| `error`  | `data: string`       | An error occurred (e.g. command not found).                 |

```json theme={null}
{"type": "start", "pid": 1234}
{"type": "stdout", "data": "step 1 complete\n"}
{"type": "stderr", "data": "warning: deprecated flag\n"}
{"type": "exit", "exit_code": 0}
```

<Note>
  The connection closes automatically once the server sends an `exit` message. You do not need to close it manually.
</Note>

## Python SDK

The SDK wraps the WebSocket protocol and yields events as an iterator:

```python theme={null}
from wrenn import Capsule

with Capsule(wait=True) as capsule:
    for event in capsule.commands.stream("python", args=["-u", "train.py"]):
        if event.type == "stdout":
            print(event.data, end="")
        elif event.type == "stderr":
            print("[err]", event.data, end="")
        elif event.type == "exit":
            print(f"\nExited with code {event.exit_code}")
```

## Errors

| Status | Meaning                                                                   |
| ------ | ------------------------------------------------------------------------- |
| `404`  | Capsule not found or does not belong to your team.                        |
| `409`  | Capsule is not in the `running` state. Start or resume the capsule first. |
