---
title: "Activity & events"
description: "How the Wavelength SDK exposes wallet history and real-time state changes through its activity stream and event model."
canonical: https://wavelength.lightning.engineering/concepts/activity-and-events/
---

> Docs index: https://wavelength.lightning.engineering/llms.txt

# Activity & events

## The activity stream

Every deposit, payment, and exit shows up in one ordered history. Wavelength keeps that history as a stream you can snapshot once or subscribe to for live updates as operations settle.

Wallet history in Wavelength is an **activity stream**: a sequence of **Entry** objects, one per user-visible wallet operation. Deposits, Lightning receives, sends, and cooperative exits each create an entry when they start and update in place as they settle.

You can read history in two ways:

- `list({ view: 'activity' })` for a paginated snapshot. Filter with `pendingOnly` or `kinds`, and page with `limit` and the string list cursor:

  ```ts
  const result = await client.list({
    view: 'activity',
    kinds: ['receive', 'deposit'],
  });
  const { activity } = result;
  // activity.entries, activity.hasMore, activity.nextCursor
  ```

- `startActivity()` for a live stream that pushes updates until you call `stopActivity()`.

For UI work, prefer the live stream. Call `subscribe()` first to register a listener, then open the stream with the entry kinds you need. Persist the last numeric `Entry.cursor` you processed and pass it back to resume after that position:

```ts
await client.startActivity({
  includeExisting: true,
  kinds: ['send', 'receive'],
});

await client.startActivity({
  kinds: ['send', 'receive'],
  cursor: lastCursor,
});
```

Each update arrives as a **WavelengthEvent** with `type: 'activity'` and the changed entry as payload. Pending entries mutate (status, phase, txid, confirmation height) until they reach `complete` or `failed`.

> **Note**
>
> `stopActivity()` closes the stream but leaves your `subscribe()` listener registered. You can call `startActivity()` again later without re-subscribing.

Direct clients also receive a terminal `activityStream` event when a stream ends or fails. The client does not retry automatically, so a direct consumer chooses its own retry policy. The current bridges do not provide structured gap cursor or reason details.

`WalletEngine` manages recovery for UI consumers. It reconciles the snapshot with `list()`, retries from its last safe numeric stream cursor with bounded backoff, and resets that cursor with the runtime lifecycle. Replayed activity events trigger idempotent snapshot refreshes instead of direct row appends. This provides eventual snapshot consistency after reconnects without duplicating entries. The React `useWalletActivity` hook reads that engine snapshot, ordered newest-first, and re-renders as reconciliation completes.

## Event model

Beyond activity rows, the client emits runtime and log events on the same subscription. Narrow on the event type before reading its payload.

**WavelengthEvent** is a discriminated union. Narrow on `type` before reading `payload`:

All five event types arrive through the same `subscribe(listener)` callback registered once.

| `type`             | Payload                                                | Meaning                                                                       |
| ------------------ | ------------------------------------------------------ | ----------------------------------------------------------------------------- |
| `'activity'`       | `Entry`                                                | A wallet operation was created or updated.                                    |
| `'activityStream'` | `{ state: 'ended' }` or `{ state: 'failed', message }` | The activity stream ended or failed without a consumer-initiated close.       |
| `'log'`            | `{ level, message }`                                   | Daemon log line forwarded for debugging (`debug`, `info`, `warn`, `error`).   |
| `'runtimeReady'`   | none                                                   | The embedded runtime finished loading and can accept `start()`.               |
| `'runtimeStopped'` | none                                                   | The runtime stopped, including an unsolicited stop surfaced by the transport. |

Activity events carry the full entry shape:

- **kind**: high-level category (see below).

- **status**: collapsed outcome: `pending`, `complete`, or `failed`.

- **amountSat** / **feeSat**: principal and fees in satoshis (plain int64 magnitudes; use the entry’s **kind** to determine direction).

- **cursor**: the monotonic stream position of this update. It is zero for entries returned outside the subscription path.

- **progress**: optional lifecycle metadata (phase, payment hash, txid, confirmation height, VTXO outpoint, and Lightning preimage).

- **request**: optional echo of what the user initiated (Lightning invoice, on-chain address, etc.).

- **failureReason** / **failureCode**: populated when status is `failed`. **failureCode** is one of:

  - **`timed_out`**: the operation exceeded its deadline before completing.
  - **`expired`**: the underlying invoice, address, or quote expired before it was used.
  - **`refunded`**: a swap or payment failed and funds were returned to the wallet.
  - **`needs_intervention`**: the operation stalled in a state that requires manual follow-up.
  - **`failed`**: a generic failure not covered by the other codes.

When known, `EntryProgress.preimage` is the hex-encoded Lightning payment preimage. For a completed Lightning-backed send, it is proof of payment for the invoice.

**Entry kinds** group operations for filtering and display:

- **`deposit`**: on-chain boarding (`deposit()` address flow).
- **`receive`**: inbound Lightning receive settled through an atomic swap into Ark.
- **`send`**: outbound payment (Lightning send swap, cooperative on-chain leave, or in-Ark send).
- **`exit`**: cooperative leave of VTXOs to a plain on-chain address.

For a `send` entry, `entry.request?.type` (`'lightning'`, `'onchain'`, or `'ark'`) tells you which rail the payment actually used; when preparing a send, `PrepareSendResult.rail` reports the same thing ahead of time. See the [wavelength-core reference](/reference/wavelength-core/) for the full field list.

**Entry phases** sit one level below status and describe *where* a pending operation is in its pipeline. They do not replace status; use both. Common phases include:

- **`request_created`**: the wallet recorded the intent but nothing external has happened yet.
- **`waiting_for_payment`**: waiting for a counterparty (Lightning payer, swap funder, or boarding confirmation).
- **`waiting_for_confirmation`**: an on-chain tx is seen but not yet deep enough in the chain.
- **`payment_detected`** / **`settling`**: value is in motion through Ark, Lightning, or swap machinery.
- **`confirmed`**: terminal success from the backing subsystem’s perspective.
- **`refunding`** / **`refunded`**: swap timeout or cancellation path returning funds.
- **`failed`**: terminal failure.

You can switch on the progress phase for structured UI states, or render the phase label directly when you want the daemon’s short label without maintaining a mapping table. Field definitions live in the [wavelength-core reference](/reference/wavelength-core/).

For the React hook signatures built on this stream, see the [wavelength-react reference](/reference/wavelength-react/). For how activity and events relate to the client’s broader startup and connection state, see [Wallet lifecycle & auth](/concepts/wallet-lifecycle-and-auth/).
