> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.getdial.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.getdial.ai/_mcp/server.

# Stream account events

> Consume a live stream of account events (inbound messages, completed calls) from any SDK or the CLI.

For anything beyond a single wait, open a **long-lived event stream**. You'll receive every event on your account as it happens — `message.received` for inbound SMS, `message.status_changed` when an outbound message is delivered, rejected, or read, `call.status_changed` as a call rings, is answered, and terminates, `call.ended` when a call finishes, `call.transcribed` when a call's transcript is ready, and more. Every event shares one envelope (`id`, `object`, `type`, `version`, `createdAt`, `relatedObject`) with a `type`-specific `data` payload. Read fields from `event.data`; `relatedObject.url` points at the REST resource (e.g. the call) for the canonical state.

## Delivery semantics

The event stream is a **presence-based notification channel, not a durable at-least-once queue.** If your connection drops, events that occurred while you were disconnected are **replayed when you reconnect within 2 minutes** — short blips are covered, but longer gaps can miss events. For guaranteed, at-least-once delivery, register a [webhook](/documentation/platform/webhooks) — Dial POSTs each event to your HTTPS endpoint, signed and retried.

## From an SDK

Every SDK exposes the same shape: ask the client for an events connection, iterate it, and close it. The connection handles subscription and token renewal for you — you never see the transport.

```python title="Python"
from dial_sdk import DialClient, DialConfig

dial = DialClient(DialConfig(api_key="sk_live_..."))
async with dial.new_events_connection() as conn:
    async for event in conn:
        if event["type"] == "message.received":
            print("inbound:", event["data"]["from"], event["data"]["body"])
        elif event["type"] == "call.transcribed":
            # thin event — fetch the transcript from the call resource
            call = dial.get_call(event["data"]["callId"])
            print("transcript:", call["transcript"])
```

```typescript title="Node"
import { DialClient } from "@getdial/sdk";

const dial = new DialClient({ apiKey: process.env.DIAL_API_KEY! });
const conn = await dial.newEventsConnection();
try {
  for await (const event of conn) {
    if (event.type === "message.received") {
      console.log("inbound:", event.data.from, event.data.body);
    } else if (event.type === "call.transcribed") {
      const call = await dial.getCall(event.data.callId);
      console.log("transcript:", call.transcript);
    }
  }
} finally {
  await conn.close();
}
```

The connection yields every event on the channel; filter by `type` in your loop. It stays open until you close it (Python's `async with` closes automatically).

> `dial wait-for` / `events/wait` field filters address `data` fields by name — `-f channel=sms` matches `event.data.channel`, unchanged from before.

## From the CLI

To capture events on a machine without writing code, run the [listen service](/documentation/cli/listen-service) — a background daemon that appends every event to a local log your agent can tail.

```bash
dial listen install   # start the background daemon
dial listen status    # see recent events
```

## Low-level (REST)

If you're building your own client, `POST /api/v1/listen/subscribe` mints a short-lived, account-scoped subscription token (`subscribeKey`, `channel`, `token`, `ttlSeconds`). Re-mint before the TTL expires to keep the stream open. The SDKs do this for you behind `new_events_connection()`, so reach for the REST endpoint only when porting to a new language.