nextmail.live_events
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.
live_events.stream()
Follow the mailbox as it changes
def stream( *, types: Sequence[LiveEventType] | None = None, address: str | None = None, last_event_id: str | None = None, api_key: str | None = None, timeout: float | None = None,) -> Generator[LiveEventResource, None]Opens the live event stream, GET /live-events as server-sent events, and yields one event per step 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. Each event is a dict with id, type, occurredAt and data, where data['addresses'] names the addresses it concerns and threadId and messageId the conversation and the message, where there is one. No content travels in an event, so read the conversation with threads.get or the message with threads.get_message when you need it.
On Nextmail it is a generator for a for loop, and on AsyncNextmail an async generator for async for. Nothing is sent until the loop starts.
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 the generator 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, the generator yields an event whose type is reset before the next events: anything may have changed in between, so list again what you keep in sync. Break out of the loop, close the generator or cancel the task that reads it, and the connection is closed. On AsyncNextmail, a loop inside contextlib.aclosing closes it the moment the loop ends, where a bare break leaves that to the event loop.
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.
Parameters
typesSequence[LiveEventType]Only these kinds of event:
message.received,message.sent,thread.changed,thread.deletedandmessage.status_changed, the values ofLIVE_EVENT_TYPES, sent comma separated. Left out, all of them. Aresetis always delivered.addressstrOnly the events of this address. One the key does not reach delivers nothing rather than failing.
last_event_idstrResume after this event id, the
idof an event an earlier stream yielded. Left out, the stream starts with what happens from now on.api_keystrOverrides the client's API key for this stream only.
timeoutfloatSeconds each attempt may take to connect and send its request, overriding the client's
timeout, and0turns that limit off. An open connection is watched for silence instead: when no byte arrives for 60 seconds, it is dropped and opened again. An attempt that runs out of time is retried like any other failure.
Returns
Generator[LiveEventResource, None] on Nextmail and AsyncGenerator[LiveEventResource, None] on AsyncNextmail, yielding each event as a dict 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 None). A reset is a LiveResetEvent: its id is the point the stream carries on from, occurredAt the moment it was yielded and data['reason'] is expired or unknown.
Example
from nextmail import nextmail for event in nextmail.live_events.stream(types=['message.received']): if event['type'] == 'reset': print('Resync: the stream could not resume, so list the inbox again') continue if event['type'] == 'message.received': thread = nextmail.threads.get(event['data']['threadId']) print(event['data']['addresses'], thread['id'], event['data']['labelIds']) breakNotes
One key, app or session holds at most 5 open streams, and a workspace at most 50. Past that the server answers 429
too_many_streams, and the generator waits and tries again.The server sends a keepalive every 25 seconds and a
retryhint 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-Afterasks, up to five minutes. The client'smax_retriesdoes not apply here.Any other 4xx, such as a 401 for an expired or revoked credential, raises
NextmailApiErrorinstead of retrying, so a revoked key ends the stream on the next attempt to open it. A success that is not an event stream raises it too, withcodeset tounrecognised_response.
Google Workspace