Live events
Follow a mailbox as it changes over one open stream, resume where you left off, and wait for a message such as a sign-in code.
The stream
GET /live-events holds the response open as server-sent events and sends one event each time the mailbox changes, so a program stays in sync without polling. It is plain HTTP, so every Nextmail SDK reads it with the client it already has, and a browser can open it with EventSource. It needs threads:read.
| Event | When |
|---|---|
| message.received | A message was filed for an address. labelIds says where. |
| message.sent | A message sent from an address was filed in its Sent folder. emailId is the send. |
| thread.changed | A conversation gained or lost labels: read or unread, starred, archived, moved, snoozed or labelled, with addedLabelIds and removedLabelIds. |
| thread.deleted | A conversation was deleted for good. Moving it to the bin is a change that adds TRASH. |
| message.status_changed | An email sent from an address was delivered, delayed, bounced, complained of, failed, suppressed or cancelled, with status and the recipients it is about. |
Each event is { id, type, occurredAt, data }, and data names the addresses it concerns, the threadId and the messageId. No content travels in the stream: read the conversation with GET /threads/{id} or the message with GET /threads/{id}/messages/{messageId} when you need it. types keeps only some kinds of event, comma separated, and address only one address.
nextmail live-events stream --types message.receivedResuming and resetting
Every event carries its id as the SSE id, a number that only grows. Send the last one you handled back as the Last-Event-ID header (or lastEventId in the query) and the events you missed are replayed first, then a ready event marks the switch to live ones. The SDKs and EventSource do this on every reconnect.
- Events can be replayed for 10 minutes and at most 1,000 back. Further back, or with an id the stream never sent, a
resetevent comes first: list again what you keep in sync, then carry on from the stream. - A comment line keeps the connection open every 25 seconds. When events you filtered out moved the stream on, it carries an
idtoo, so a quiet filter never falls behind. - The server ends every stream after 30 minutes, and when the credential stops being valid, with an
endevent that says why. Open it again withLast-Event-ID.
Who sees what
An API key, the access token of an app somebody connected, or a session can open the stream. A key or an app limited to particular addresses gets only the events of the addresses it reaches, checked on the server for every event, and its stream is checked again at most once a minute while events flow, so a revoked or narrowed key stops receiving within a minute.
One key, app or session holds at most 5 open streams, and a workspace at most 50. Past that, opening one is refused with 429 too_many_streams, so filter one stream by type and address rather than opening several.
Waiting for a message
GET /threads/wait holds the request open for up to 60 seconds and answers the first message that arrives and matches: from the sender's address or name, subject text in the subject, address the address it was delivered to, and since the moment to look from. It is how a script or an agent waits for a sign-in code it asked a service to send. message is null when nothing matched in time, so ask again to keep waiting.
- In the SDKs it is
threads.waitForEmail, and on the command linenextmail threads wait-for-email --from service.example --subject code --timeout 60. - Over MCP and in the assistant it is the
waitForEmailtool, read only, besidereadVerificationCodefor reading the code out of the message. - The wait wakes on the live events of the mailbox and looks again at least every 10 seconds, so a match is answered within moments of arriving.
Google Workspace