rtClient.push
SDK methods for Web Push: a device subscribes, and the notification feed reaches its lock screen.
Web Push subscriptions for the signed-in person. What gets pushed is the protocol's notification
feed - the same items rtClient.users.getNotifications returns - so there
is no separate push vocabulary. Each item goes through the app it belongs to, or through the hub when
the person has no subscription in that app, and never through both.
The browser half is not here. Asking permission, registering with the push service and
unsubscribing on sign-out belong to usePushSubscription in
@rising-tide/ui/react, which calls these methods itself. Use them directly only for a settings
screen that lists or removes devices.
Nothing is sent until the server has a key. The protocol ships with push switched off. While no
VAPID key is configured, getVapidPublicKey answers 503 - read that as "push is not offered" and
hide the ask. No device can subscribe until then, because a browser needs that key to subscribe at
all; devices subscribe once push is switched on.
Every write acts on the caller's own devices. An app-scoped API key may only subscribe devices for its own app.
Methods
rtClient.push.getVapidPublicKey()
The key to pass as applicationServerKey when the browser subscribes. Returns
ApiResponse<{ publicKey: string }>, or 503 when push is not offered.
const { data } = await rtClient.push.getVapidPublicKey();
const applicationServerKey = data.publicKey;rtClient.push.subscribe(input)
Save this device's subscription for the signed-in person in one app. Returns
ApiResponse<{ subscription: PushSubscriptionView }>.
const json = browserSubscription.toJSON();
await rtClient.push.subscribe({
appId: 10, // this app's id
endpoint: json.endpoint!,
keys: { p256dh: json.keys!.p256dh, auth: json.keys!.auth },
userAgent: navigator.userAgent, // optional - shown in the device list
});Subscribing the same device again updates it in place, and a device another person used moves to
the caller. A person holds at most ten devices per app. An eleventh is refused with 409
TOO_MANY_DEVICES; removing a device is the person's choice, so render the message and point them
at their device list rather than retrying.
rtClient.push.unsubscribe(target)
Stop pushing to a device. Pass this device's endpoint (sign-out, a toggle), or an id from
listSubscriptions to remove another of the caller's devices. Returns ApiResponse<{ removed: boolean }>.
await rtClient.push.unsubscribe({ endpoint: json.endpoint! });
await rtClient.push.unsubscribe({ id: 42 });rtClient.push.listSubscriptions()
Every device the signed-in person receives push on, across every app. Returns
ApiResponse<{ subscriptions: PushSubscriptionView[] }>. The endpoint itself is never returned.
const { data } = await rtClient.push.listSubscriptions();
for (const device of data.subscriptions) {
// device.id, device.appId, device.appName, device.userAgent, device.lastSuccessAt
}rtClient.push.recordOpen(deliveryId)
Record that the signed-in person tapped a push and landed here. Returns
ApiResponse<{ recorded: boolean }>. Recorded once, and only against the caller's own delivery.
RisingTideProviders calls this itself when the opened URL carries ?via=push&pd=<deliveryId>, then
removes both parameters. An app only has to put that marker on the URL its service worker opens.
What a service worker receives
A push event's event.data.json() is a PushPayload:
| Field | What it is |
|---|---|
v | The payload version, currently 1 |
item | The NotificationItem itself, so the worker writes the notification in the app's own words |
more | Further items folded into this push; an app may say "and N more" |
fallback | { title, body }, where body may be null - an app-neutral sentence. Show it for any item.kind the app does not recognise |
badge | The unread count for navigator.setAppBadge, or null when only a floor is known |
deliveryId | Put it on the URL a tap opens, as ?via=push&pd=<deliveryId> |
A push must always display something. Safari revokes push permission from a site whose push
shows nothing, so a worker that does not recognise a kind shows fallback rather than returning
early. An app that declares offers.push in its conformance config is checked for this, for a
notificationclick handler, and for calling disable() on sign-out.