Skip to content

Push Subscriptions

To receive Push notifications in the user’s browser, you need to register a subscription from the frontend.

  • Your app’s VAPID public key (found in the app detail screen in the dashboard)
  • An API key with subscribe_only scope

First, create a Service Worker file.

public/sw.js
self.addEventListener("push", (event) => {
const data = event.data?.json() ?? {};
event.waitUntil(
self.registration.showNotification(data.title ?? "Notification", {
body: data.body,
icon: data.icon,
badge: data.badge,
data: { url: data.url },
})
);
});
self.addEventListener("notificationclick", (event) => {
event.notification.close();
if (event.notification.data?.url) {
event.waitUntil(clients.openWindow(event.notification.data.url));
}
});
import { PushCF } from "@todoke/sdk";
const client = new PushCF({
apiKey: "pk_subscribe_only_key",
});
// Wait for Service Worker to be ready
const registration = await navigator.serviceWorker.ready;
// Register subscription (browser notification permission dialog appears)
await client.subscribe({ registration });

There is no SDK-style endpoint for unsubscribing; use the app-scoped endpoint that includes the appId in the URL (DELETE /api/v1/apps/:appId/subscriptions).

// REST API (appId in the URL, endpoint in the body)
await fetch(`https://api.todoke.dev/api/v1/apps/${appId}/subscriptions`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer pk_subscribe_only_key",
},
body: JSON.stringify({ endpoint: subscription.endpoint }),
});

Sending a registration request with the same endpoint does not create duplicate records. Records are upserted keyed on endpoint, and you can tell new from updated by the HTTP status in the response.

CaseHTTP Status
Registering a new endpoint201
Updating info for an existing endpoint200

The examples so far used the SDK-style endpoint that determines the app automatically from the API key (POST /api/v1/subscriptions; the SDK-style endpoint is POST-only and has no unsubscribe endpoint). Separately, you can also use endpoints that include the appId in the URL.

Terminal window
# Register (POST /api/v1/apps/:appId/subscriptions)
curl -X POST https://api.todoke.dev/api/v1/apps/{appId}/subscriptions \
-H "Authorization: Bearer pk_subscribe_only_key" \
-H "Content-Type: application/json" \
-d '{ "endpoint": "...", "p256dh": "...", "auth": "..." }'
# Unsubscribe (DELETE /api/v1/apps/:appId/subscriptions)
curl -X DELETE https://api.todoke.dev/api/v1/apps/{appId}/subscriptions \
-H "Authorization: Bearer pk_subscribe_only_key" \
-H "Content-Type: application/json" \
-d '{ "endpoint": "..." }'
SDK-style (/api/v1/subscriptions)App-scoped (/api/v1/apps/:appId/subscriptions)
Supported operationsRegister (POST) onlyRegister / unsubscribe (POST / DELETE)
UnsubscribeNone (use the app-scoped endpoint DELETE /api/v1/apps/:appId/subscriptions)
appIdDerived automatically from the API keySpecified as a URL parameter
When the key’s app and appId don’t match(N/A)403 APP_MISMATCH
Subscriber count limit checkNoneYes (see below)

The Free plan can hold up to 1,000 active subscriptions per app. This limit check is performed only on the app-scoped endpoint (POST /api/v1/apps/:appId/subscriptions), and if the limit has already been reached when you try to register a new endpoint, a 429 SUBSCRIBER_LIMIT_EXCEEDED (with upgrade_url) is returned.

For error code details, see Error Codes & Limits.

BrowserStatus
Chrome / Edge
Firefox
Safari 16.4+ (macOS / iOS)
Safari 16.3 and earlier