> For the complete documentation index, see [llms.txt](https://docs.talus.network/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.talus.network/talus-docs-v2.1.0/guides/nexus-api/tutorial/03-stream-events.md).

# 3️⃣ Stream Events

{% hint style="info" %}
**Audience:** Someone who can list executions and now wants to know the moment one changes.

**Goal:** Open the Server-Sent Events stream, filter it to the kinds you care about, then reconnect under the provider's published cursor and retention contract.
{% endhint %}

```mermaid
sequenceDiagram
  participant Client as Browser or dApp
  participant Relay as Same-origin relay
  participant API as Nexus API stream
  participant Log as Durable event log
  Client->>Relay: Open /events/stream with events:read
  Relay->>API: Add server-side API key and open stream
  API->>Log: Start at tip or requested cursor
  Log-->>API: Event frames and keep-alive comments
  API-->>Relay: id, kind, and decoded data
  Relay-->>Client: id, kind, and decoded data
  Client-->>Relay: Connection closes
  Client->>Relay: Reconnect with Last-Event-ID or last_event_id
  Relay->>API: Forward cursor with server-side API key
  API->>Log: Replay events after cursor
  Log-->>API: Retained frames when available, then live tail
  API-->>Relay: Retained replay or live tail
  Relay-->>Client: Validated frames or provider error
```

The sequence explains why the cursor belongs to the provider's public stream contract rather than the browser: a provider may replay retained events after a validated cursor, while keep-alive comments distinguish a quiet network from a dead connection and are not application events. Validate event ids and deduplicate locally; retention, reset behavior, duplicate delivery, and keep-alive cadence are provider-specific. Consult the deployment's `/openapi.json` and published event-log policy.

### Keep the API key out of curl arguments

Create one restrictive curl configuration file for this page instead of expanding `NEXUS_API_KEY` into each stream command. Hidden input keeps the terminal from echoing the value; the trap removes the file and unsets the variable, while same-user process inspection can still observe the key until the shell or child process exits:

```bash
umask 077
API_CURL_CONFIG="$(mktemp)"
chmod 600 "$API_CURL_CONFIG"
cleanup_api_key() {
  rm -f -- "$API_CURL_CONFIG"
  unset NEXUS_API_KEY API_CURL_CONFIG
}
trap cleanup_api_key EXIT
read -r -s NEXUS_API_KEY
printf '\n' >&2
printf 'header = "x-api-key: %s"\n' "$NEXUS_API_KEY" >"$API_CURL_CONFIG"
node --input-type=module - "$API_CURL_CONFIG" <<'NODE'
import { statSync } from "node:fs";
if ((statSync(process.argv[2]).mode & 0o777) !== 0o600) process.exit(1);
NODE
```

Do not type `export NEXUS_API_KEY='...'`, put a real key in command history, or commit the configuration file; the stream commands pass only its path to curl.

### Open the stream

`GET /events/stream` is a standard [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) endpoint (`text/event-stream`). It requires the `events:read` scope and is exempt from ordinary request-timeout handling; the client, provider, or transport can still close the connection, so use the cursor/reconnect behavior below:

```bash
curl --config "$API_CURL_CONFIG" -sSN "$NEXUS_API_URL/events/stream"
```

Each frame carries one self-contained event for parsing — no extra fetch is needed merely to decode the frame:

```
id: 4217
event: message
data: {"id":4217,"kind":"DAGCreatedEvent","tx_digest":"...","sender_address":"0x...","payload":{...}}
```

A fresh connection starts at the current tip: you receive only what happens from now on. A provider may emit keep-alive comments on its documented cadence; do not assume a universal interval:

```
: keep-alive
```

`curl -N` shows these; a browser's `EventSource` swallows them, which is the correct behavior for a comment frame.

The frame is a wake/invalidation signal, not a second database. After parsing it, re-read the authoritative resource through the relay; projection lag or replay can make the frame arrive before the REST view catches up.

### Filter by kind

Omit `kinds` and you get every event on the network. The Run Watcher only cares about runs, so narrow the feed — comma-separated kind names, trailing `Event` suffix optional:

```bash
curl --config "$API_CURL_CONFIG" -sSN "$NEXUS_API_URL/events/stream?kinds=RequestWalkExecution,WalkAdvanced,ExecutionFinished"
```

The kind strings are the on-chain event names — the same `kind` field you see in each frame. An unknown kinds value fails before the stream opens with HTTP 400 and `INVALID_FILTER_VALUE`; it does not produce a silent stream.

### Kill it and resume under the provider contract

The frame's `id` is a cursor supplied by the provider's event-log contract. Note the last validated `id`, `Ctrl-C` the stream, wait a bit, then request retained replay after that id when the provider documents that behavior:

```bash
curl --config "$API_CURL_CONFIG" -sSN "$NEXUS_API_URL/events/stream?last_event_id=4217"
```

Retained events may replay first and then the stream may go live, but the provider defines retention, duplicate delivery, and reset behavior. Browsers automatically echo the last id back as the `Last-Event-ID` header on reconnect, but that transport behavior is not a guarantee that the provider can replay the cursor. Validate and deduplicate complete frames locally. If the provider rejects a stale or reset cursor, discard it and restart at the current tip only when the provider documents that recovery; do not assume that every invalid cursor is clamped.

When the provider documents current-tip recovery, clear `last_event_id` entirely before opening the replacement stream:

```bash
curl --config "$API_CURL_CONFIG" -sSN "$NEXUS_API_URL/events/stream"
```

This cursor is a request for provider-defined retained replay, not a promise of a complete history. Re-read the authoritative REST resource after reconnecting, because projection lag or retention boundaries can make the event stream incomplete.

{% hint style="success" %}
**Checkpoint.** Open a filtered stream in one terminal, trigger anything in another (or just wait for network traffic), and confirm: frames have validated ids, your consumer deduplicates them, a provider-documented cursor replay or stale-cursor reset behaves as advertised, and an unknown kind name returns HTTP 400 `INVALID_FILTER_VALUE`. If a valid stream stays quiet, [Troubleshooting → A silent stream](/talus-docs-v2.1.0/guides/nexus-api/troubleshooting.md#a-silent-stream) decodes it.
{% endhint %}

### Close this step when finished

After the stream demonstration, clean the configuration and disarm its trap. This explicit boundary makes the three tutorial pages safe to concatenate in one shell while retaining the `EXIT` cleanup if any earlier command fails:

```bash
cleanup_api_key
trap - EXIT
unset -f cleanup_api_key
```

Next: [assemble the Run Watcher](/talus-docs-v2.1.0/guides/nexus-api/tutorial/04-assemble-the-dapp.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.talus.network/talus-docs-v2.1.0/guides/nexus-api/tutorial/03-stream-events.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `build a script that syncs our docs to a CMS` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
