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

# Events

> Handle channel events, the event wildcard, and connection lifecycle events.

Add handlers with one of three methods, based on what you want to catch:

* **`onChannel` / `on_channel`** — events on one channel.
* **`onAnyChannel` / `on_any_channel`** — every channel event, on any channel.
* **`onLifecycle` / `on_lifecycle`** — connection state: `open`, `close`, `reconnect`, `error`.

## Channel events

A channel event has an `id` (the subscription it came from), a `channel`, and `data` (the decoded payload).

<CodeGroup>
  ```typescript TypeScript theme={null}
  radion.realtime.onChannel("trading", (e) => {
    console.log(e.id, e.channel, e.data);
  });

  // every channel event
  radion.realtime.onAnyChannel((e) => console.log(e.channel, e.data));
  ```

  ```python Python theme={null}
  @radion.realtime.on_channel("trading")
  async def on_trade(e):
      print(e.id, e.channel, e.data)

  # every channel event
  @radion.realtime.on_any_channel()
  async def on_any(e):
      print(e.channel, e.data)
  ```

  ```rust Rust theme={null}
  use futures_util::StreamExt;
  use radion_sdk::realtime::{Channel, Payload, Subscription};

  let mut trading = radion
      .realtime
      .subscribe(Subscription::new("trading", Channel::Trading))
      .await?;
  while let Some(e) = trading.next().await {
      match e.data {
          Payload::Trading(trade) => println!("{} {} {:?}", e.id, e.channel, trade.kind),
          _ => {}
      }
  }

  // every channel event
  let mut all = radion.realtime.events();
  while let Some(e) = all.next().await {
      println!("{} {:?}", e.channel, e.data);
  }
  ```
</CodeGroup>

In TypeScript, `onChannel(name, …)` narrows `event.data` to that channel's typed payload; the catch-all `events()` stream yields `AnyChannelPayload`. In Python `ChannelEvent` is generic over the payload and defaults to `Any`. In Rust `data` is the typed `Payload` enum, so `match` on it and the compiler checks you handle every case.

## Lifecycle events

| Event       | Payload                                                                                                 |
| ----------- | ------------------------------------------------------------------------------------------------------- |
| `open`      | —                                                                                                       |
| `close`     | `{ code, reason }`                                                                                      |
| `reconnect` | `{ attempt, delayMs }` (TS) / `{ "attempt", "delay" }` (Python) / `Reconnect { attempt, delay }` (Rust) |
| `error`     | An `Error` (often `RadionServerError`) / `Error(RadionError)` (Rust)                                    |

<CodeGroup>
  ```typescript TypeScript theme={null}
  radion.realtime.onLifecycle("open", () => console.log("connected"));
  radion.realtime.onLifecycle("close", ({ code, reason }) =>
    console.log("closed", code, reason)
  );
  radion.realtime.onLifecycle("reconnect", ({ attempt, delayMs }) =>
    console.log(`reconnect #${attempt} in ${delayMs}ms`)
  );
  radion.realtime.onLifecycle("error", (err) => console.error(err));
  ```

  ```python Python theme={null}
  @radion.realtime.on_lifecycle("open")
  async def opened(_):
      print("connected")

  @radion.realtime.on_lifecycle("close")
  async def closed(info):
      print(info["code"], info["reason"])

  @radion.realtime.on_lifecycle("error")
  async def errored(err):
      print(err)
  ```

  ```rust Rust theme={null}
  use futures_util::StreamExt;
  use radion_sdk::realtime::LifecycleEvent;

  let mut lifecycle = radion.realtime.lifecycle();
  while let Some(event) = lifecycle.next().await {
      match event {
          LifecycleEvent::Open => println!("connected"),
          LifecycleEvent::Close { code, reason } => println!("closed {code} {reason}"),
          LifecycleEvent::Reconnect { attempt, delay } => {
              println!("reconnect #{attempt} in {delay:?}");
          }
          LifecycleEvent::Error(err) => eprintln!("{err}"),
          _ => {}
      }
  }
  ```
</CodeGroup>

In Python, handlers can be sync or async. In TypeScript, each `on*` method returns the client, so you can chain calls. In Rust, channel and lifecycle events come as `Stream`s (`events()`, per-subscription `subscribe(...)`, and `lifecycle()`); drop the stream to stop reading. To remove a handler, use the matching `off*` method (`offChannel` / `off_channel`, `offAnyChannel` / `off_any_channel`, `offLifecycle` / `off_lifecycle`). Leave out the handler to remove all of them for that target.

<Note>
  If one of your handlers throws, the SDK reports it through the `error` event
  and does not retry it. It never blocks the other handlers from getting the
  event.
</Note>
