---
title: "client.LiveEvents"
description: "Every method in this namespace: its signature, its parameters, what it returns and an example."
url: "https://nextmail.sh/docs/csharp/reference/live-events"
area: "C#"
category: "Reference"
---

# client.LiveEvents

Every method in this namespace: its signature, its parameters, what it returns and an example.

## Methods

Follow the mailbox as it changes, without polling: one event each time a message arrives or is sent and each time a conversation changes or is deleted, resumed where it left off after a dropped connection.

### `LiveEvents.StreamAsync`

Follow the mailbox as it changes

```csharp
IAsyncEnumerable<JsonObject> StreamAsync(
    IEnumerable<string>? types = null,
    string? address = null,
    string? lastEventId = null,
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Opens the live event stream, `GET /live-events` as server-sent events, and yields one event at a time for as long as you read it: `message.received` when a message is filed for an address, `message.sent` when a message sent from an address is filed in its Sent folder, `thread.changed` when a conversation gains or loses labels (read, starred, archived, moved, snoozed or labelled), `thread.deleted` when one is deleted for good, and `message.status_changed` when the delivery of an email sent from an address moves on, with its `status`: `delivered`, `delivery_delayed`, `bounced`, `complained`, `failed`, `suppressed` or `cancelled`, as in `Nextmail.Constants.LiveEventTypes` and `LiveSendStatuses`. Each event is a `JsonObject` with `id`, `type`, `occurredAt` and `data`, and `data` holds `addresses`, the addresses it concerns, with `threadId` and `messageId`, the conversation and the message where there is one. No content travels in an event, so read the conversation with `Threads.GetAsync` or the message with `Threads.GetMessageAsync` when you need it.

The stream never ends by itself. The server closes it after 30 minutes, when the credential stops being valid, or when the connection drops, and `StreamAsync` opens it again and resumes where it left off through `Last-Event-ID`: at once after the server closes it, and with backoff after a failure. When the server can no longer replay from that point, which happens once the event is more than 10 minutes or 1,000 events back, it yields an event whose `type` is `reset` before the next ones: anything may have changed in between, so list again what you keep in sync. Break out of the `await foreach` loop to close the stream, or cancel `cancellationToken`, which closes it and throws `OperationCanceledException`.

A key or an app limited to particular addresses gets only the events of the addresses it reaches, enforced on the server for every event.

Scopes: `threads:read`.

**Parameters**

- `types` (`IEnumerable<string>?`): Only these kinds of event, such as `LiveEventTypes.MessageReceived`: `message.received`, `message.sent`, `thread.changed`, `thread.deleted` and `message.status_changed`. Left out or empty, all of them. A `reset` always comes.
- `address` (`string?`): Only the events of this address. One the key does not reach delivers nothing rather than failing.
- `lastEventId` (`string?`): Resume after this event id, the `id` of an event an earlier stream yielded. It goes out as the `Last-Event-ID` header. Left out, the stream starts with what happens from now on.
- `apiKey` (`string?`): Overrides the client's API key for this stream, on every reconnect too.
- `cancellationToken` (`CancellationToken`): Closes the stream. The loop then throws `OperationCanceledException`, also while it waits to reconnect.

**Returns**

An `IAsyncEnumerable<JsonObject>` that yields one object per event, each with `id`, `type`, `occurredAt` and `data`. `data` holds `addresses`, `threadId` and `messageId`, and by type `labelIds` (received), `emailId` (sent), `addedLabelIds` and `removedLabelIds` (changed), or `emailId`, `status` and `recipients` (status changed, where `threadId` may be null). A `reset` event has the id to resume from as its `id`, `type` set to `reset`, the moment the client read it as `occurredAt`, and `reason` in `data`: `expired` or `unknown`, as in `LiveResetReasons`.

**Example**

```csharp
using Nextmail.Constants;

await foreach (var change in client.LiveEvents.StreamAsync(types: new[] { LiveEventTypes.MessageReceived }))
{
    if ((string?)change["type"] == "reset")
    {
        Console.WriteLine("Resync: the stream could not resume");

        continue;
    }

    var thread = await client.Threads.GetAsync(change["data"]!["threadId"]!.GetValue<string>());

    Console.WriteLine($"{thread["id"]} arrived for {change["data"]!["addresses"]![0]}");

    break;
}
```

**Notes**

- One key, app or session holds at most 5 open streams, and a workspace at most 50. Past that the stream is refused with 429 `too_many_streams`, and `StreamAsync` keeps trying.
- The server sends a keepalive every 25 seconds and a `retry` hint of 3 seconds. Events are kept for resuming for 10 minutes and at most 1,000 per workspace.
- A network failure, a 408, a 429 and a 5xx answer are retried for as long as you read, waiting about a second at first and doubling up to 30 seconds, or longer when `Retry-After` asks, up to 5 minutes. When no byte arrives for 60 seconds, `StreamAsync` drops the connection and opens it again. `MaxRetries` does not apply to the stream.
- A 400, 401, 403, 404 or 422 throws the `NextmailApiException` instead of retrying, so an expired or revoked credential ends the stream on the next attempt to open it. A 200 that is not an event stream throws it too, with `Code` set to `unrecognised_response`.
- The client's `Timeout` bounds only the wait for the response headers, so it never cuts an open stream short.

Also available in: API [`GET /live-events`](https://nextmail.sh/docs/api/reference/live-events#get-live-events); TypeScript [`liveEvents.stream()`](https://nextmail.sh/docs/sdk/reference/live-events#stream); Python [`live_events.stream()`](https://nextmail.sh/docs/python/reference/live-events#stream); Ruby [`live_events.stream`](https://nextmail.sh/docs/ruby/reference/live-events#stream); PHP [`liveEvents->stream`](https://nextmail.sh/docs/php/reference/live-events#stream); Go [`LiveEvents.Stream`](https://nextmail.sh/docs/go/reference/live-events#stream); Java [`liveEvents().stream`](https://nextmail.sh/docs/java/reference/live-events#stream); CLI [`nextmail live-events stream`](https://nextmail.sh/docs/cli/reference/live-events#live-events-stream).
