puter.events.workers

Websites Puter Apps Node.js Workers

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

An events worker runs an app's published handlers. It is a per-app artifact, not a per-handler one: an app with five published handlers still has exactly one events worker behind them.

A hosted Puter deployment may bill an events worker as a standing monthly cost, one charge per app that has one — publishing handlers you no longer use keeps that meter running even if nothing ever delivers to them. This surface is where an app owner sees what it is running and stops paying for one it does not need.

const { items } = await puter.events.workers.list();      // [{ appUid, appName, handlerCount, ... }]
await puter.events.workers.destroy(items[0].appUid);      // removes every handler that app published

list() takes no appUid: an account session or API token sees every app you own, while an app sees only its own worker (0 or 1 item), and only when the signed-in user owns that app — the same scope destroy() already used.

list()

puter.events.workers.list()
puter.events.workers.list(options)
  • options.limit (Number): Apps per page.
  • options.cursor (String): The cursor from a previous page. Omit to start from the first page.

Resolves to { items, cursor, deployable }. Each item is { appUid, appName, appTitle, handlerCount, createdAt, updatedAt, script } — createdAt is when the app's earliest handler was published (its events worker's birth), updatedAt is its most recently published or updated handler, and script names the deployed script, useful when reporting an issue. cursor is present only while more pages exist. deployable reports whether this server actually deploys events workers at all — false on a self-hosted install that has not turned the runtime on, in which case handlers can still be published but nothing ever runs a background delivery for them.

destroy()

puter.events.workers.destroy(appUid)

Removes every handler the named app has published, in one call — the same consequences as calling puter.events.handlers.remove() on each of them: a name nothing is bound to is deleted outright, and a name with subscriptions on it is deleted with those subscriptions suspended (suspendedReason: 'handler_not_found'), never dropped. Publishing new handlers for the app afterwards resumes them, exactly as republishing a single removed handler would.

It also retires the worker session it was running background deliveries under, for every holder — see onPersistent() — the same session revoking it from the user's sessions list would end. The session is not gone for good: the first delivery after a republish mints a fresh one.

Resolves to { appUid, removed, suspended } — removed is how many handlers were deleted, suspended how many subscriptions that left suspended across all of them. An app with nothing published rejects with events_handler_not_found.

Errors

Both methods reject with { message, code }:

code Meaning
events_worker_owner_only list() was called with a scoped access token, such as the one in a getReadURL() URL, whichever account or app issued it.
events_handler_not_found destroy() named an app with no published handlers.
events_handler_forbidden The caller does not own the app named to destroy(), or is a scoped access token — and an app that is not there answers the same way.
too_many_requests Over the handler publish/remove or listing budget.
events_disabled Events are not enabled on this server.
events_failed The server answered with something the SDK could not make sense of.

Example

List an account's events workers, and destroy one that is no longer needed

<html>
<body>
    <script src="https://js.puter.com/v2/"></script>
    <script>
        (async () => {
            const name = `retired-${puter.randName()}`;
            const app = await puter.apps.create(name, `https://example.com/${name}`);

            await puter.events.handlers.publish(
                'ingestUpload',
                async ({ event, ctx }) => {
                    await fetch(ctx.endpoint, { method: 'POST', body: event.path });
                },
                { appUid: app.uid },
            );

            const { items, deployable } = await puter.events.workers.list();
            puter.print(`this server deploys events workers: ${deployable}<br>`);
            for (const worker of items) {
                puter.print(`${worker.appName}: ${worker.handlerCount} handler(s)<br>`);
            }

            const destroyed = await puter.events.workers.destroy(app.uid);
            puter.print(`removed ${destroyed.removed}, suspended ${destroyed.suspended}<br>`);
        })();
    </script>
</body>
</html>