Skip to content
Content
Skill

/websocket-notifications

Sending real-time notifications from the API to the Admin app over websockets, and reacting to them on the client. Use this skill when the developer wants to push a message to the user who triggered some server-side work (e.g. per-entry progress from a background task/bulk

BOOST
From plugin
webiny-js
8k76 skills3 MCP
Install
$ npx -y skills add webiny/webiny-js --skill websocket-notifications --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/websocket-notifications

Context preview

The summary Claude sees to decide when to auto-load this skill.

Sending real-time notifications from the API to the Admin app over websockets, and reacting to them on the client. Use this skill when the developer wants to push a message to the user who triggered some server-side work (e.g. per-entry progress from a background task/bulk

SKILL.md

websocket-notifications.SKILL.md
name: webiny-websocket-notifications
description: >
  Sending real-time notifications from the API to the Admin app over websockets, and
  reacting to them on the client. Use this skill when the developer wants to push a
  message to the user who triggered some server-side work (e.g. per-entry progress from a
  background task/bulk action) and show a toast, update a cache, or refresh UI in response.
  Requires Webiny 6.5.0 or newer.

Websocket notifications (API → Admin)

TL;DR

On the **API**, inject `WebsocketsSendToIdentityUseCase` (`webiny/api`) + `IdentityContext` (`webiny/api/security`) and call `sendToIdentity.execute({ id }, { action, data })`. On the **Admin**, implement a `WebsocketEventHandler` (`webiny/admin/websockets`) that filters on `action` and reacts (e.g. toast via `Notifications` from `webiny/admin`), registered with `createFeature` + `RegisterFeature`.

API — emit

import { WebsocketsSendToIdentityUseCase } from "webiny/api";
import { IdentityContext } from "webiny/api/security";

class MyTaskOrHook {
  constructor(
    private identityContext: IdentityContext.Interface,
    private sendToIdentity: WebsocketsSendToIdentityUseCase.Interface
  ) {}

  async notify(entry) {
    // Best-effort: a websocket failure must never fail the real work.
    try {
      const identity = this.identityContext.getIdentity();
      if (identity) {
        await this.sendToIdentity.execute(
          { id: identity.id },
          {
            action: "cms.product.discountApplied",
            data: { id: entry.entryId, price: entry.values.price }
          }
        );
      }
    } catch (ex) {
      // log & swallow
    }
  }
}
// dependencies: [IdentityContext, WebsocketsSendToIdentityUseCase]
  • Send to the user who triggered the work — get them from `IdentityContext`. In a

background task/bulk action, the triggering identity is available.

  • Use a namespaced `action` string; put the payload in `data`.
  • Sender data type: `{ action?: string; data?: T; error?: {...} }`.

Admin — listen

import { WebsocketEventHandler } from "webiny/admin/websockets";
import { Notifications } from "webiny/admin";

const ACTION = "cms.product.discountApplied";

class MyHandlerImpl implements WebsocketEventHandler.Interface {
  constructor(private notifications: Notifications.Interface) {}

  async handle(event: WebsocketEventHandler.Event): Promise<void> {
    const payload = event.payload as { action?: string; data?: { id: string; price: number } };
    if (payload.action !== ACTION || !payload.data) {
      return; // every handler sees every message — filter by action
    }
    this.notifications.success({
      title: "Discount applied",
      description: `New price ${payload.data.price}.`
    });
  }
}

export const MyHandler = WebsocketEventHandler.createImplementation({
  implementation: MyHandlerImpl,
  dependencies: [Notifications]
});

Read the message off `event.payload` — `event.payload.action` and `event.payload.data` (the exact `{ action, data }` object the API sent).

Admin — register

Register the handler in a feature and render it from your `Admin.Extension`:

import { createFeature, RegisterFeature } from "webiny/admin";
import { MyHandler } from "./MyHandler.js";

const MyFeature = createFeature({
  name: "MyExtension/Notifications",
  register(container) {
    container.register(MyHandler);
  }
});

export default () => <RegisterFeature feature={MyFeature} />;

The websockets runner resolves every registered `WebsocketEventHandler` and calls `handle` for each incoming message — hence the `action` filter in each handler.

Related

  • `webiny-cms-bulk-actions` — the typical emitter: a bulk action's `processData` sends a

message per processed entry so the Admin can toast progress live.

Read more
Ships withwebiny-js

Open-source content platform. Self-hosted on AWS serverless. Built as a TypeScript framework you extend with code, not a closed product you configure through a UI. Runs on Lambda, DynamoDB, S3, and CloudFront inside your own AWS account. Scales automatically.

Get the whole plugin
Stats
8,048
Stars
682
Forks
Active
Maintenance
TypeScript
Language
2h ago
Last commit
8y ago
Created
9h ago
Added

Repo: webiny/webiny-js

Other skills on webiny-js.