puter.events.fetch()

Websites Puter Apps Node.js Workers

The Events API is in beta. Event shapes, limits, and behavior may change between releases.

Reads events a subject already recorded, a page at a time. This is how a client catches up after being closed, offline, or asleep — a subscription only delivers while something is listening, and fetch() is what fills the gap.

It is a plain query. Nothing is registered, no position is stored for you, and calling it twice returns the same answer: you keep the cursor and pass it back as after.

Only subjects with a store behind them can answer, which today means notif: alone — the notification mailbox. fs: and kv: keep no log, and asking for one is refused with fetch_unsupported_subject rather than answered with an empty page you would read as "nothing happened".

Syntax

puter.events.fetch(options)

Parameters

options (Object) (required)

  • subject (String) (required): What to read. notif:account for your account's notifications, notif:app-user for the ones belonging to the app you are running as, or the fully qualified notif:<appId>:<audience>. Audiences are account, developer (about an app, to whoever owns it), and app-user (about your data inside an app).
  • after (String): The cursor from a previous page. Leave it off to start from the oldest notification still kept.
  • limit (Number): Events per page. Capped at 200; defaults to 50.

Return value

A Promise for { items, cursor }:

  • items — the events, oldest first, in the same shape a live delivery has.
  • cursor — pass it as after to read the next page. It is absent when there is nothing after this page, which is how you know you are caught up.

Each item is a notification event:

Field Type Description
id String The notification's uid. The same id the live delivery of that notification carries, so a client that reconnects mid-catch-up can drop the duplicate.
subject String notif:<appId or userId>:<audience> — the slice of the mailbox it belongs to.
op String Always post.
uid String The notification's uid, as the mailbox names it.
type String What kind of notification it is, from the published catalog — share.received, app.worker.deployed, and so on.
audience String account, developer, or app-user.
appUid String | null The app it is about, or null for one from the platform.
notification Object The payload — title, text, icon, fields.
self Boolean Always true: a mailbox is your own.
ts Number When it was created, in milliseconds since the epoch.
seq Number Position within the page.

An app sees only what its audience allows: account notifications — email changed, credits exhausted, an account action — are never returned to an app, whatever subject it names, and developer notifications only where the recipient owns the app. Nothing is refused for asking; a slice you may not see comes back empty, so the call cannot be used to find out what exists.

Notifications are kept for 14 days. A fetch reads inside that window, so a client away longer than that starts from what is left rather than from where it stopped.

The promise rejects with { message, code }fetch_unsupported_subject for a family with no store, invalid_subject or invalid_subject_audience for one that does not parse, too_many_requests over the fetch budget, events_disabled where events are off.

Examples

Catch up on everything missed

<html>
<body>
    <script src="https://js.puter.com/v2/"></script>
    <script>
        (async () => {
            let after;
            let seen = 0;
            do {
                const page = await puter.events.fetch({
                    subject: 'notif:account',
                    after,
                });
                for (const event of page.items) {
                    puter.print(`${event.type}: ${event.notification.title}<br>`);
                    seen++;
                }
                after = page.cursor;
            } while (after);

            if (!seen) puter.print('nothing missed<br>');
        })();
    </script>
</body>
</html>

Read the missed ones, then keep listening

<html>
<body>
    <script src="https://js.puter.com/v2/"></script>
    <script>
        (async () => {
            const seen = new Set();

            const show = (event) => {
                if (seen.has(event.id)) return;
                seen.add(event.id);
                puter.print(`${event.notification.title}<br>`);
            };

            // Live first, so nothing arriving during the catch-up is lost —
            // `id` is what makes the overlap harmless.
            const sub = await puter.events.onLocal('notif:account',
                ({ event }) => show(event));

            const page = await puter.events.fetch({ subject: 'notif:account' });
            page.items.forEach(show);

            await sub.off();
        })();
    </script>
</body>
</html>