DEV Community

sekera-radim
sekera-radim

Posted on Originally published at impri.dev

Web Push Notifications for Agent Approvals

Wire browser-native web push into Impri so a pending AI agent action pops a real OS notification on your desktop the instant it lands, no Slack workspace or polling tab required.


Why polling a tab isn't enough

An on-call incident-response agent doesn't wait for business hours. It watches your alerting feed, drafts a remediation (restart a pod, roll back a deploy, silence a noisy alert) and pushes it to Impri as a pending action. If the only way you find out is by refreshing the web inbox, you'll miss the ones that matter — the whole point of gating the agent's actions is that a human actually looks at them before they run.

Impri's web push support closes that gap. It's built directly into the web inbox at app.impri.dev (or your self-hosted instance) using the standard Web Push API and VAPID — no Firebase project, no Google account, no extra SaaS dependency. Keep the inbox open in a pinned tab and your OS notification tray does the rest.


How the subscription flow works

The browser talks to two things: the browser's own push service (Chrome/FCM, Safari/APNs, etc.) and Impri's /v1/push endpoints. Once a tab subscribes, Impri pushes to it on every new pending action — no polling loop needed on the client side.

// Run once, e.g. when the ops dashboard tab loads and the user opts in
const { public_key: vapidKey } = await fetch('/v1/push/vapid-public-key').then(r => r.json())

const registration = await navigator.serviceWorker.ready
const subscription = await registration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: vapidKey,
})

const { endpoint, keys } = subscription.toJSON()

await fetch('https://api.impri.dev/v1/push/subscribe', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${IMPRI_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ endpoint, keys }),
})
Enter fullscreen mode Exit fullscreen mode

That's the entire client integration. From here, every pending action your incident-response agent creates with POST /v1/actions triggers a push to this browser session, and the OS shows a native notification even if the tab is backgrounded.


Server setup if you self-host

Impri Cloud has web push enabled out of the box. Self-hosting it yourself means generating a VAPID key pair and setting three environment variables before the endpoints will respond:

npx web-push generate-vapid-keys   # prints a public + private key pair

# then set on the server:
VAPID_PUBLIC_KEY=BNpW...
VAPID_PRIVATE_KEY=...
VAPID_SUBJECT=mailto:oncall@yourcompany.com
Enter fullscreen mode Exit fullscreen mode

Without these, GET /v1/push/vapid-public-key reports { "enabled": false } and the browser subscribe step never fires — the inbox just falls back to normal polling, so nothing breaks, you simply don't get the popup. See self-hosting for the rest of the deployment.


What actually triggers a push

Only genuinely new pending actions fire a push — action.created events where status="pending". If a rule in your project auto-approves or auto-rejects an action before a human ever needs to see it, no push is sent, because there's nothing to review. This matters for an incident agent tuned with rules for low-risk remediations (like silencing a known-flaky alert) versus ones that always need a human (like a rollback) — only the latter buzzes your desktop.

Each subscription is tied to a specific browser session and project. Log out, or the push service invalidates the endpoint, and it stops receiving pushes — no manual cleanup required beyond calling DELETE /v1/push/subscribe if you want to revoke it explicitly.


Web push vs. the other channels

Notification channels Web push
Best for Team-wide alerts (Slack, Telegram, ntfy) An operator with the inbox open
Setup POST /v1/notification-channels, once per project Browser pushManager.subscribe(), per session
Digest batching Yes No — always immediate

Web push isn't a replacement for a team notification channel — it's for the person actually watching the queue. Most teams running an always-on agent pair both: ntfy or Telegram so someone always gets pinged, and web push so whoever has the dashboard open sees it the second it lands.


Next step

Full API reference — endpoints, response shapes, and the notification-channels comparison table — lives in web push. If you haven't wired up the base approval loop yet, start with quickstart to get an API key and push your first gated action.

Top comments (0)