The server keeps a durable event log for message, chat, group, and poll changes. Every event has an increasingDocumentation Index
Fetch the complete documentation index at: https://docs.photon.codes/docs/llms.txt
Use this file to discover all available pages before exploring further.
sequence.
Location updates are not in this log. They are delivered only through im.locations.watch(...). Updates missed while disconnected cannot be recovered with catchUp(...).
What You Can Do
| Need | Use this when |
|---|---|
| Recover missed events | Your process starts or reconnects and needs missed message, chat, group, or poll events |
| Store a checkpoint | You need to resume from the last successfully processed event |
| Consume live streams | You want to read SDK streams with for await or .on(...) |
| Derive streams | You want to split streams with .filter(...), .map(...), or .take(...) |
Recovery Flow
In production, use this order:- Read
lastHandledSequencefrom your database. - Immediately open the live
subscribeEvents(...)streams you need. - At the same time, call
im.events.catchUp(lastHandledSequence)to replay missed events. - Feed live events and catch-up events into the same bounded-concurrency queue.
- Deduplicate by
sequence, and advance the saved checkpoint only after all earlier sequences have completed successfully.
catchUp(...) to finish before opening live streams. Historical recovery and live consumption should run together so new events do not fall into a gap while you are catching up.
Concurrent Recovery
During recovery, put catch-up events and live events into one processing queue. The queue may process events concurrently, but checkpoint storage must advance in contiguous sequence order. This example opens message, chat, group, and poll live streams while catch-up is running:catchUp(...) returns TypedEventStream<CatchUpEvent>. If you omit lastHandledSequence, replay starts from the beginning of the log. lastHandledSequence must be a non-negative safe integer; invalid cursors throw ValidationError before the network call.
The catch-up stream ends with catchup.complete:
catchup.complete to overwrite your checkpoint. It only means the historical replay is done. Your live streams are already open and continue receiving new events.
seenSequences prevents processing the same event twice when it appears in both catch-up and live streams. completedSequences ensures the checkpoint only stores the largest sequence for which every earlier event has also completed.
Events have the same shape in catch-up and live streams. Write method return values are still the authoritative result for the write your code just performed; event streams are for observing other changes and asynchronous state.
Stream Consumption
Every server-streaming SDK method returnsTypedEventStream<T>. This includes subscribeEvents(...), catchUp(...), watch(...), and downloadStream(...).
One stream instance has one consumer. Do not consume the same stream with both for await and .on(...). If you need multiple branches, derive them first with .filter(...), .map(...), or .take(...).
for await
for await loop closes the underlying stream.
await using
await using closes the stream when the scope exits. Node.js 18.17 does not support await using natively; on the minimum supported runtime, use try / finally or break out of for await.
.on(...)
stop() cancels the loop and closes the stream. The callback may be async; the SDK waits for the current callback to finish before delivering the next event. If you omit onError, stream errors are thrown asynchronously and cannot be caught by an outer try / catch.
Derived Streams
.filter(...) keeps matching events:
.map(...) transforms events:
.take(n) emits the first n events, then closes the parent stream:
TypedEventStream
| Member | Purpose |
|---|---|
for await (const event of stream) | Default consumer; claims exclusive consumption |
.on(callback, onError?) | Callback consumer; returns stop() |
.filter(predicate) | Derives a child stream with matching events |
.map(transform) | Derives a transformed child stream |
.take(count) | Derives the first count events and closes the parent when done |
.close() | Closes the underlying network request; safe to call more than once |
await using stream = ... | Closes the stream when the scope exits |
Next Steps
- Messages — subscribe to message events
- Chats — subscribe to chat events
- Groups — subscribe to group events
- Polls — subscribe to poll events
- Error Handling — handle stream disconnects, retries, and idempotent writes