---
sidebar_position: 2
title: Logs
---

# Logs

Cloud workflow runs stream to CloudWatch in real time. The CLI exposes them via Server-Sent Events (SSE) — `zibby workflow logs <uuid> -t` is a thin client over that.

## Tail live

```bash
zibby workflow logs <uuid> -t
```

What you see:

```
  Streaming logs for workflow 2b1ea07f-3ede-4bfd-a51d-431f0bab008e...
  Press Ctrl+C to stop.

2026-05-02 23:30:51.345  zibby v0.1.x
────────────────────────────────────────────────────────────
 Workflow:  my-pipeline
 Job:       ee333411-...
 Project:   6b60049d-...
 Agent:     cursor (model: auto)
────────────────────────────────────────────────────────────
[setup] Bundle extracted (3.2s)
[setup] Loaded MyPipelineWorkflow
[setup] Registered 5 agent strategies (...)

┌ example
│ ◆ Model: auto | key: ***bc97
│ Prompt sent to LLM:
│ ...
│ ◆ status: warn
└ done 19.4s
[done] my-pipeline completed in 19.4s
```

## Heroku-style follow

UUID is a *workflow* identifier, not an execution identifier. With `-t`, after one execution finishes the stream waits for the next trigger of the same workflow and auto-switches:

```
  Waiting for next execution...

  ┌─ Execution: cd1f55d5...43d7 (task: 3b85ee3a)
  └─ Streaming logs...

(streams the new execution)
```

Ctrl+C to exit. There's no "exit on completion" mode — if you only want logs from one specific run, dump (without `-t`):

```bash
zibby workflow logs <uuid>
```

## Reconnects

The SSE client reconnects automatically on transient errors. You'll see at most one `SSE Error:` + `Reconnecting...` per outage; once recovered, `Reconnected.` is printed once and the stream continues. No spam during cold-start flap.

## Past runs

CloudWatch retains logs for 30 days by default. Beyond that, the per-run session folder (uploaded to S3 at the end of each execution) is the long-term archive — accessible via `zibby workflow download <uuid>`.

## Programmatic access

The same SSE endpoint is consumable from your own code:

```js
const eventSource = new EventSource(
  `https://logs-stream.zibby.app/?jobId=${uuid}`,
  { headers: { Authorization: `Bearer ${apiKey}` } }
);

eventSource.addEventListener('log', (e) => {
  const { timestamp, message } = JSON.parse(e.data);
  console.log(timestamp, message);
});

eventSource.addEventListener('complete', () => {
  console.log('Run finished');
  eventSource.close();
});
```

Or use the polling endpoint for batch retrieval:

```bash
GET /v1/workflows/<uuid>/logs?lines=1000&since=<unix-ms>
```

## Filtering by job ID

`uuid` matches the workflow. To pin to a specific execution, pass the job ID instead:

```bash
zibby workflow logs <jobId>
```

The CLI auto-detects: if it's an execution row, you stream that one. If it's a workflow row, you get follow-mode.
