Browser Push Notifications
SendSeven lets you collect browser push subscribers and send them notifications through campaigns and flows. There are two ways to ask visitors for push permission:
| Hosted by SendSeven | On your own domain | |
|---|---|---|
| Installation | Nothing to install | One file + one script tag |
| Permission prompt | Opens in a SendSeven-hosted popup window (your-workspace.push.sendseven.com) | Shown directly on your page |
| Notification sender shown by the browser | Your SendSeven push subdomain | Your own domain |
| Trigger moment | Via SendSeven widgets | Anywhere — automatically, or from your own buttons and code (e.g. after checkout) |
| Works on hosted site builders without file access | Yes | No (you must be able to serve a file from your webroot) |
Both modes can be used side by side. Subscribers are stored in the same lists either way.
Browser push permission is bound to the exact origin it was granted on. Subscribers collected on your SendSeven push subdomain keep working there, but they do not transfer to your own domain — the own-domain list starts fresh.
Prerequisites
- A SendSeven account with the Browser Push channel (created automatically for every workspace)
- A widget ID — any widget from Settings → Widgets works; the SDK uses it to load your branding and default lists
- For own-domain mode: the ability to place a file at the root of your website
Own-domain setup
Step 1 — Add the service worker file
Create a file at the root of your website (the file must be served from your own origin, at the top level, so it can control the whole site):
importScripts('https://widget.sendseven.com/service-worker.js');
That single line is the whole file. The actual push-handling logic is loaded from SendSeven's CDN, so you never need to update this file — we ship improvements automatically.
Browsers refuse service worker scripts that are served via a redirect. /sendseven-sw.js must answer with 200 OK directly. A server-side rewrite or proxy rule is fine; a 301/302 is not.
Already have a service worker? If your site is a PWA with an existing service worker, don't add a second file — add the importScripts(...) line at the top of your existing service worker instead, and set data-sw-path (below) to its path.
Step 2 — Add the SDK snippet
Add this before the closing </body> tag on every page where you want push functionality:
<script src="https://widget.sendseven.com/push-sdk.js"
data-widget-id="YOUR_WIDGET_ID" async></script>
Script tag attributes
| Attribute | Required | Default | Description |
|---|---|---|---|
data-widget-id | Yes | — | Widget ID used for branding, language, and default lists |
data-auto | No | false | "true" shows the branded permission prompt automatically |
data-delay | No | 5 | Seconds to wait before the automatic prompt |
data-sw-path | No | /sendseven-sw.js | Path to the service worker file on your origin |
data-lists | No | — | Comma-separated list IDs new subscribers are added to |
data-language | No | channel setting | "auto" follows the visitor's browser language, or pass a locale code (ar, de, en, es, fr, hr, it, ja, ko, nl, pl, pt, ru, tr, zh). Omit it to use the language configured on the Browser Push channel |
data-fallback-language | No | en | Locale used when the visitor's language is not supported |
The prompt's title, description and buttons are translated automatically; any custom texts you configured on the Browser Push channel always win over the translation.
With data-auto="true" the SDK shows a branded, non-blocking "soft ask" card first; the browser's native permission dialog only appears after the visitor clicks Allow. If the visitor dismisses the card, it is not shown again automatically for 7 days.
Step 3 — Verify the installation
In the SendSeven app, open Settings → Channels → Browser Push → Settings → Install on your website, enter your domain, and click Verify installation. SendSeven checks that:
https://your-domain.com/sendseven-sw.jsis reachable,- it is served without a redirect, and
- it imports the SendSeven service worker.
JavaScript API
Once loaded, the SDK exposes window.SendSevenPush:
SendSevenPush.prompt(options?)
Shows the branded soft-ask card, then (on Allow) the native permission dialog, then subscribes. Returns a Promise.
const result = await SendSevenPush.prompt({
listIds: ['LIST_ID_1', 'LIST_ID_2'], // optional, overrides data-lists
email: '[email protected]', // optional, links to an existing contact
externalId: 'user-4711' // optional, your own user reference
});
// result: { subscribed: boolean, reason?: string }
SendSevenPush.subscribe(options?)
Same options and return value as prompt(), but skips the soft-ask card and goes straight to the native permission dialog. Use this when your own UI already asked the question — for example a "Notify me about my order" button on the order confirmation page:
document.querySelector('#order-updates').addEventListener('click', async () => {
const { subscribed } = await SendSevenPush.subscribe({
listIds: ['ORDER_UPDATES_LIST_ID'],
email: order.customerEmail
});
if (subscribed) showToast('You will be notified about your order!');
});
Passing email stores the address with the subscriber: if no contact with that email exists yet, a contact is created with it (instead of an anonymous push subscriber). If a contact with that email already exists, the subscription is kept separate and the address is stored as a claimed email on the subscription — because the visitor's identity isn't verified, SendSeven never links a browser to an existing contact based on an email alone. You can merge them manually in the app.
SendSevenPush.isSubscribed()
Returns Promise<boolean> — true when permission is granted and an active push subscription exists.
SendSevenPush.unsubscribe()
Removes the browser-side push subscription. Returns Promise<boolean>.
Result reasons
prompt() / subscribe() resolve with { subscribed: false, reason } when subscription did not happen:
| Reason | Meaning |
|---|---|
dismissed | Visitor clicked "Not now" on the soft-ask card |
denied | Browser permission is (or was previously) denied |
unsupported | Browser does not support push notifications |
sw_missing | /sendseven-sw.js was not found on your origin (check the browser console for details) |
sw_error, init_failed, subscribe_failed, save_failed | Technical failures — details in the browser console |
Calling the SDK before it has loaded
The script loads with async, so SendSevenPush may not exist yet when your code runs. Two options:
Ready event:
window.addEventListener('sendseven:push-ready', () => {
// SendSevenPush is fully available here
});
Command queue (safe to call from inline onclick handlers at any time):
<script>
window.SendSevenPush = window.SendSevenPush || { q: [] };
window.SendSevenPush.prompt = window.SendSevenPush.prompt ||
function (o) { window.SendSevenPush.q.push(['prompt', o]); };
window.SendSevenPush.subscribe = window.SendSevenPush.subscribe ||
function (o) { window.SendSevenPush.q.push(['subscribe', o]); };
</script>
<button onclick="SendSevenPush.prompt()">Get updates</button>
Queued calls are executed in order once the SDK loads. Note that queued calls cannot receive the Promise return value — use the ready event if you need the result.
The Promotion tools tab under Widgets in the SendSeven app generates copy-paste subscribe-button snippets with your lists pre-selected.
Customizing the prompt
The soft-ask card uses your Browser Push channel settings (Settings → Channels → Browser Push): company name, logo, brand color, language (12 languages supported), and per-text overrides (title, description, buttonText).
Hosted mode
Without any installation, SendSeven widgets (e.g. the newsletter widget) offer push subscription through a popup window on your workspace's push subdomain (your-workspace.push.sendseven.com). Visitors grant permission to that subdomain and notifications are delivered from it. This remains the right choice for hosted site builders where you cannot place a file in the webroot.
Limitations
- iOS Safari delivers web push only for sites the visitor has added to their home screen (an iOS platform restriction, both modes).
- The service worker file must be at a path that covers the pages where you subscribe visitors — serving it from the webroot is strongly recommended. Deeper scopes require the
Service-Worker-Allowedresponse header. - Subscribers are origin-bound: switching between hosted and own-domain mode does not migrate existing subscribers.
Troubleshooting
| Symptom | Cause / fix |
|---|---|
reason: 'sw_missing' and a console error | The stub file is missing, or your server answers unknown paths with index.html (common on SPAs — add an explicit route for /sendseven-sw.js) |
| Verify fails with "redirect" | Your server redirects the file (e.g. to a canonical host or trailing slash). Serve it with a direct 200 |
Prompt never appears with data-auto | Permission was previously denied in the browser, or the visitor dismissed the card within the last 7 days |
| Notification permission dialog blocked by the browser | Some browsers require a user gesture — trigger subscribe()/prompt() from a click handler |