Skip to content
Setup guide · about 5 minutes

Install Pusharoo on any website

Two files, one line of HTML. Works on any site where you can put a file at the root of your domain and add a script tag — plain HTML, PHP, WordPress, Laravel, Next.js, you name it.

What you need: your site on HTTPS, the ability to place a file at https://yoursite.com/sw.js, and a site key from the Pusharoo dashboard — create a free account to get one (it takes a minute).

1. Add your site and get a site key

In the dashboard go to Websites → Add website. Enter the exact address visitors use, e.g. https://yourstore.com (the www. version is accepted automatically). Add your logo URL so your prompt and notifications carry your brand — Pusharoo never shows its own name to your visitors.

You’ll get a public site key like pk_3f9a…. It’s safe to put in your HTML: it only works from the website address you registered (requests from any other origin are refused).

2. Upload sw.js to your site root

Browsers only deliver notifications to a service worker that lives on your domain. Download ours and upload it so it opens at https://yoursite.com/sw.js:

Download sw.js

Or from a terminal on your server:

curl -o /path/to/your/webroot/sw.js https://pusharoo.com/sw.js

Serve it as JavaScript and don’t let it be cached for long. On Apache, add this to your site’s .htaccess:

<Files "sw.js"> Header set Cache-Control "no-store, max-age=0" Header set Service-Worker-Allowed "/" </Files>

3. Add the snippet

Paste this before </head> on every page (or in your site’s global header/footer):

<script src="https://pusharoo.com/sdk/pusharoo.js" data-site="pk_your_site_key" async></script>

That’s it. After a few seconds a polite, branded prompt appears. The browser’s own permission dialog is only shown after the visitor taps Allow on yours — never on page load. A visitor who taps “Not now” isn’t asked again for a few days, and a browser that says “Block” is never asked again.

A fuller example with timing options:

<script src="https://pusharoo.com/sdk/pusharoo.js" data-site="pk_your_site_key" data-delay="8000" data-scroll="30" data-title="Want first dibs on deals?" data-text="We send 1–2 alerts a week. Unsubscribe anytime." data-ios-hint="true" async></script>

4. iPhone & iPad (iOS / iPadOS 16.4+)

Apple only allows web push from sites that have been added to the Home Screen. A regular Safari or Chrome tab on iPhone can’t subscribe. The visitor’s steps are:

  1. Tap Share → Add to Home Screen.
  2. Open your site from the new Home Screen icon.
  3. Tap Allow on your prompt, then on the iOS dialog.

For this to work your site needs a small web app manifest. Save this as /manifest.webmanifest (or /manifest.json) at your site root, with your own name and icons:

{ "name": "Your Store", "short_name": "Your Store", "id": "/", "start_url": "/", "scope": "/", "display": "standalone", "background_color": "#ffffff", "theme_color": "#ffffff", "icons": [ { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" } ] }

…and link it from your <head>:

<link rel="manifest" href="/manifest.webmanifest"> <link rel="apple-touch-icon" href="/icon-192.png"> <meta name="apple-mobile-web-app-capable" content="yes">

Set data-ios-hint="true" on the snippet to show iPhone visitors a friendly how-to card automatically.

Prompt options

AttributeDefaultWhat it does
data-siterequiredYour site key from the dashboard (pk_…).
data-delay6000Milliseconds to wait before showing the soft prompt.
data-scroll0Also wait until the visitor has scrolled this % of the page (0 = off).
data-cap-days3After “Not now”, wait this many days before asking again.
data-max-dismiss3Stop asking automatically after this many “Not now” taps.
data-autotrueSet to false to never prompt automatically — only from your own button via Pusharoo.subscribe().
data-title“Get updates from {Your site}?”Prompt headline.
data-textfriendly defaultPrompt body text (or set it per site in the dashboard).
data-allow / data-laterAllow / Not nowButton labels.
data-positionautoauto (top-right on desktop, bottom sheet on phones), top or bottom.
data-ios-hintfalsetrue shows iPhone visitors a short “Add to Home Screen” how-to card.
data-sw/sw.jsPath of your service worker if it isn’t /sw.js.
data-debugfalseLog what the snippet is doing to the console.

Already have a service worker?

A page can only have one service worker per scope, so don’t register a second one. Instead add these two lines at the top of your existing worker and point the snippet at it:

self.PUSHAROO_SITE = 'pk_your_site_key'; importScripts('https://pusharoo.com/sw.js');
<script src="https://pusharoo.com/sdk/pusharoo.js" data-site="pk_your_site_key" data-sw="/your-existing-sw.js" async></script>

Cloudflare & caching

If your site is behind Cloudflare (or any CDN), make sure /sw.js and your manifest are never cached at the edge, so updates reach visitors right away. In Cloudflare: Caching → Cache Rules → Create rule:

Also make sure Rocket Loader or other script optimizers don’t rewrite sw.js. The snippet registers the worker with updateViaCache: 'none', so the browser always checks for a fresh copy.

JavaScript API & events

The snippet exposes window.Pusharoo so you can build your own “Get alerts” button or bell:

<button onclick="Pusharoo.subscribe()">🔔 Get deal alerts</button> Pusharoo.state(); // "default" | "subscribed" | "granted" | "denied" | "unsupported" | "ios-needs-home-screen" Pusharoo.subscribe(); // shows your soft prompt now (ignores the frequency cap) Pusharoo.prompt(); // shows the prompt only if the frequency cap allows Pusharoo.unsubscribe(); // unsubscribes this browser

And it fires browser events you can hook into (analytics, UI, your CRM):

window.addEventListener('pusharoo:prompt-shown', () => {}); window.addEventListener('pusharoo:subscribed', e => console.log(e.detail.endpoint, e.detail.isNew)); window.addEventListener('pusharoo:dismissed', e => {}); window.addEventListener('pusharoo:permission', e => console.log(e.detail.permission)); window.addEventListener('pusharoo:unsubscribed', () => {});

Tags & events

Tell Pusharoo about a subscriber so you can segment and automate on it. Calls made before someone subscribes are queued in the browser and sent when they do.

Pusharoo.tag('plan', 'pro'); // one tag Pusharoo.tag({ plan: 'pro', city: 'Toronto' }); // several at once (up to 30) Pusharoo.untag('plan'); // remove Pusharoo.track('added_to_cart'); // a custom event (letters, numbers, _ . - :) Pusharoo.track('purchase');

Use them in Segments (“Tag” and “Did event”), as automation triggers (“A custom event happens”, “A tag is set”), and as a stop condition (“Stop if event: purchase” cancels a waiting cart reminder). Your server can send the same tags and events with the REST API.

Webhooks

Add an HTTPS webhook URL to any website in the dashboard and Pusharoo will POST JSON to it for subscribe, unsubscribe, click (with variant and button) and custom event events. Works with Zapier and Make: paste a “Catch Hook” URL as the webhook. Failed deliveries are retried with backoff (up to 6 attempts).

POST https://your-server.com/pusharoo-hook X-Pusharoo-Event: subscribe X-Pusharoo-Timestamp: 1794470000 X-Pusharoo-Signature: sha256=<hex HMAC> {"event":"subscribe","site":{"id":1,"name":"Your Store","origin":"https://yourstore.com"}, "data":{"subscription_id":42,"platform":"android","locale":"en-CA","timezone":"America/Toronto"}, "ts":"2026-11-12T11:00:00+00:00"}

Verify the signature with the webhook secret shown in the dashboard:

<?php $raw = file_get_contents('php://input'); $ts = $_SERVER['HTTP_X_PUSHAROO_TIMESTAMP'] ?? ''; $sig = $_SERVER['HTTP_X_PUSHAROO_SIGNATURE'] ?? ''; $ok = hash_equals('sha256=' . hash_hmac('sha256', $ts . '.' . $raw, getenv('PUSHAROO_WEBHOOK_SECRET')), $sig) && abs(time() - (int)$ts) < 300;

Segments, schedules & automations

A/B tests & action buttons

A/B tests: in a new campaign, write a variant B title, message or image, then choose 50/50 split (everyone gets A or B) or Test on a slice, then send the winner (10–50% get A or B; after the wait you choose, the variant with the higher click-through rate goes to everyone else automatically). Nobody receives both.

Action buttons: add up to two buttons, each with its own link (e.g. “Shop now” and “See details”). Chrome, Edge and Android show them; other browsers show the notification without buttons and the main link still works. Button clicks are counted separately.

RSS-to-push

Dashboard → Integrations → Add a feed. Paste any RSS or Atom feed (WordPress /feed/, Ghost, Substack, Shopify blog .atom, YouTube channel feeds), pick the website and, optionally, a tag/event audience. New posts become notifications automatically — or drafts for you to review. Title = post title, message = summary, image = featured image, click = the post. Only posts published after you add the feed are sent, at most one per check, so a backlog never floods anyone.

REST API

Create a key in Integrations → REST API. It’s shown once and stored hashed. The API is server-to-server only (requests with a browser Origin are refused), limited to 120 requests a minute per key, and only sees your own websites.

curl https://pusharoo.com/api/v1/campaigns \ -H "Authorization: Bearer $PUSHAROO_KEY" -H "Content-Type: application/json" \ -d '{"title":"Back in stock","body":"Your size is here","url":"https://yourstore.com/p/1", "site_id":1,"filters":{"tag_key":"wishlist","tag_value":"p1"}, "buttons":[{"label":"Buy now","url":"https://yourstore.com/cart/add/1"}], "send_at":"2026-12-01T09:00:00-05:00"}'
Method & pathWhat it does
GET /api/v1/meCheck the key
GET /api/v1/sitesYour websites, site keys and active subscriber counts
GET /api/v1/segmentsSaved segments with live counts
POST /api/v1/audienceCount an audience: site_id, segment_id, filters
POST /api/v1/campaignsSend now, schedule (send_at), or "draft":true. Optional icon, image, buttons, segment_id, filters, subscriber_ids, repeat (daily/weekly/monthly), ab {mode: split|winner, title, body, image, test_pct, wait_minutes}
GET /api/v1/campaigns, GET /api/v1/campaigns/Status and stats (incl. A/B results)
POST /api/v1/campaigns//send · /cancelSend a draft (or resume) · cancel / stop
GET /api/v1/subscribersPaginate with after + limit (≤500); filter with site_id, tag_key, tag_value, event…
POST /api/v1/subscribers//tags{"tags":{"plan":"pro","trial":null}} — null removes
POST /api/v1/events{"subscriber_id":42,"event":"purchase"} — fires event automations and stop conditions
POST /api/v1/notifyTransactional: send immediately to up to 1,000 subscriber_ids (“your order shipped”)
GET /api/v1/statsActive subscribers, new this week, delivered and clicks in 30 days

Get a subscriber’s id from the pusharoo:subscribed browser event’s endpoint via your webhook (subscribe carries subscription_id), then store it on your user record.

Google Tag Manager

Create a Custom HTML tag with the snippet, trigger All Pages, publish. You still need /sw.js at your site root (download the two-line version from your website page in the dashboard). Tag and track from other tags with Pusharoo.tag() / Pusharoo.track().

WordPress

  1. Upload sw.js to the folder that contains wp-config.php (your site root) with your host’s File Manager or SFTP. Check that https://yoursite.com/sw.js opens.
  2. Add the snippet to your header: in a block theme use Appearance → Editor → Patterns/Template parts → Header with a Custom HTML block, or use any “insert headers and footers” plugin, or add it to your child theme’s header.php before </head>.
  3. Caching plugins (WP Rocket, LiteSpeed Cache, W3TC): exclude /sw.js from caching and from JS minify/combine, and exclude pusharoo.js from “delay JavaScript”.

Shopify & hosted site builders

Root file limitation: Shopify (and Wix, Squarespace, most hosted builders) don’t let you upload a file that opens at yourstore.com/sw.js. Service workers must be served from your own domain, so the standard install can’t work there as-is.

Options that do work:

Test checklist

  1. https://yoursite.com/sw.js opens and shows JavaScript.
  2. Open your site in Chrome desktop, wait for the prompt, tap Allow twice.
  3. In the dashboard, your website’s subscriber count goes up by one.
  4. Send a campaign (or use “Send test” on a subscriber) and watch it arrive.
  5. On Android Chrome, repeat. On iPhone, Add to Home Screen first.

Troubleshooting

Privacy & unsubscribing

Pusharoo stores only the browser’s push address and keys, browser type, language and timezone — no names, emails or cookies. Visitors can unsubscribe from the browser’s site settings at any time, or via your own button using Pusharoo.unsubscribe(). Addresses that browsers report as gone (HTTP 404/410) are removed from sending immediately and purged automatically. See our privacy note.

Uninstall

Remove the script tag. If you also want existing subscribers’ browsers to drop the worker, replace /sw.js with this for a few weeks before deleting it:

self.addEventListener('install', () => self.skipWaiting()); self.addEventListener('activate', () => self.registration.unregister());