By default PocketBase sends realtime events only for Record create/update/delete operations (and for the OAuth2 auth redirect), but you are free to send custom realtime messages to the connected clients via the app.SubscriptionsBroker() instance.

app.SubscriptionsBroker().Clients() returns all connected subscriptions.Client indexed by their unique connection id.

app.SubscriptionsBroker().ChunkedClients(size) is similar but return the result as a chunked slice allowing you to split the iteration across several goroutines (usually combined with errgroup ).

The current auth record associated with a client could be accessed through client.Get(apis.RealtimeClientAuthKey)

Note that a single authenticated user could have more than one active realtime connection (aka. multiple clients). This could happen for example when opening the same app in different tabs, browsers, devices, etc.

Below you can find a minimal code sample that sends a JSON payload to all clients subscribed to the "example" topic:

func notify(app core.App, subscription string, data any) error { rawData, err := json.Marshal(data) if err != nil { return err } message := subscriptions.Message{ Name: subscription, Data: rawData, } group := new(errgroup.Group) chunks := app.SubscriptionsBroker().ChunkedClients(300) for _, chunk := range chunks { group.Go(func() error { for _, client := range chunk { if !client.HasSubscription(subscription) { continue } client.Send(message) } return nil }) } return group.Wait() } err := notify(app, "example", map[string]any{"test": 123}) if err != nil { return err }

From the client-side, users can listen to the custom subscription topic by doing something like:

import PocketBase from 'pocketbase'; const pb = new PocketBase('http://127.0.0.1:8090'); ... await pb.realtime.subscribe('example', (e) => { console.log(e) })
import 'package:pocketbase/pocketbase.dart'; final pb = PocketBase('http://127.0.0.1:8090'); ... await pb.realtime.subscribe('example', (e) { print(e) })