Skip to the documentation
Knowledge base

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.

EventWhen
message.receivedA message was filed for an address. labelIds says where.
message.sentA message sent from an address was filed in its Sent folder. emailId is the send.
thread.changedA conversation gained or lost labels: read or unread, starred, archived, moved, snoozed or labelled, with addedLabelIds and removedLabelIds.
thread.deletedA conversation was deleted for good. Moving it to the bin is a change that adds TRASH.
message.status_changedAn 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.

Terminal
nextmail live-events stream --types message.received

Resuming 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 reset event 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 id too, so a quiet filter never falls behind.
  • The server ends every stream after 30 minutes, and when the credential stops being valid, with an end event that says why. Open it again with Last-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 line nextmail threads wait-for-email --from service.example --subject code --timeout 60.
  • Over MCP and in the assistant it is the waitForEmail tool, read only, beside readVerificationCode for 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.