How teams put pushr to work.
Eight teams, one HTTP request. Watch revenue, ship safely, page the right person, keep AI agents and their bills in check, and hand people the calls AI shouldn’t make.
Every demo runs in your browser. Nothing leaves the page.
Hear about the orders that matter. Sleep through the rest.
Order emails pile up until nobody reads them, so the $1,240 wholesale order looks the same as a $38 one, and a failed payment waits until morning.
- Small orders land quietly in Notification Center and never light up the screen.
- Big orders break through Focus, so they’re seen within minutes.
- The whole team shares one feed, each person with their own quiet hours.
Each order becomes one request, at the priority it deserves.
The rule lives in your code, so it can be anything: an amount, a customer tier, a product. Your team sees the same feed, each with their own Focus and quiet hours.
import { notify } from '@pushrsh/sdk';
// In your order webhook
const big = order.total >= 1000;
await notify({
title: big ? 'Big order' : 'New order',
body: `${order.number} · ${usd(order.total)}`,
priority: big ? 'high' : 'low',
url: order.adminUrl,
});Watch the deploy, then ship it from your phone.
Production deploys need a human to say yes, so someone sits watching CI, or the approval waits in a browser tab until they’re back at a laptop.
- Build and test progress stays on the Lock Screen and in the Dynamic Island.
- The pipeline stops before production and asks. One tap answers it.
- Only one answer counts, even if two people tap at once.
- Build
- Tests
- Approval
- Deploy
Merge to watch the requests go back and forth.
import { liveActivity, notify } from '@pushrsh/sdk';
const deploy = liveActivity(`deploy-${pr}`, { name: 'ci.acme' });
await deploy.start({ title: `Deploy #${pr}`, status: 'Building' });
await deploy.update({ status: 'Running tests', progress: 0.5 });
await notify({
title: `Ship #${pr} to production?`,
body: '4 commits · 214 tests passed',
actions: [
{ kind: 'callback', id: 'approve', label: 'Approve',
callbackUrl: approveUrl },
{ kind: 'callback', id: 'reject', label: 'Reject',
callbackUrl: rejectUrl, destructive: true },
],
});Page one person, not the whole team.
When the API goes down at 2 am, alerts in a shared channel either wake everyone or nobody, and the customers find out first.
- Two failed checks in a row confirm it’s down, so one blip doesn’t page anyone.
- The person on call is paged until they answer. If they don’t, the next person is.
- The public status page shows the outage, and the recovery tells everyone it’s over.
- MayaAsleepOn call this week
- SamAsleepNext on the rotation
Break the API to start the night.
import { uptime } from '@pushrsh/sdk';
// Safe to run on every deploy.
await uptime('api', {
url: 'https://api.beacon.dev/health',
every: '1m',
confirmAfter: 2,
});
// Who's on call is set in the app:
// Beacon → On-call → Maya, then Sam.Find out when the nightly job didn’t run.
A cron job that crashes sends an error. One that never starts sends nothing, and the first sign is a customer’s dashboard with yesterday’s numbers.
- Good nights send nothing at all.
- A failure, or a night the job never ran, sends one alert by 4 am.
- The next good run replaces the alert, so the feed shows exactly how long it was down.
The job pings pushr when it finishes. pushr notices when it doesn’t.
# crontab: export at 03:00, check in when it's done
0 3 * * * ./export.sh && curl -fsS \
-H "Authorization: Bearer $PUSHR_TOKEN" \
"$PUSHR_URL/heartbeat/nightly-export?every=1d&grace=1h"
# Or report the failure, with the reason
./export.sh || curl -fsS -X POST --data "$(tail -1 export.log)" \
-H "Authorization: Bearer $PUSHR_TOKEN" \
"$PUSHR_URL/heartbeat/nightly-export/fail"Let agents work alone, and ask before they act.
An agent that can change production either needs someone watching the whole time, or is trusted with steps that can’t be undone.
- The agent asks on your phone before anything risky, and waits for your answer.
- Approve, reject, or reply with what to do instead.
- It tells you when it’s finished, so nobody has to keep checking.
The agent works on its own, and checks with you when it matters.
# Claude Code
claude mcp add --transport http pushr https://pushr.sh/mcp
# Then tell your agent, or put it in CLAUDE.md / AGENTS.md:
# "Before anything you can't undo, ask me with pushr
# and wait for my answer. Notify me when you're done."Catch the runaway agent before it spends the month.
Model bills now move faster than anyone reviews them. One retry loop or a prompt that suddenly needs 10× the tokens, and the overnight spend is already gone by the time the invoice email arrives.
- Normal days send nothing. An unusual spend rate sends one push within minutes.
- Pause the agent from the notification. Your gateway gets the answer.
- At the daily budget, new runs wait until someone raises it or pauses everything.
Your gateway pushes on what’s unusual, not on every call.
import { notify } from '@pushrsh/sdk';
// In your LLM gateway, after each call
if (rate > baseline * 5 && !alerted.has(agent)) {
alerted.add(agent);
await notify({
title: `AI spend is ${Math.round(rate / baseline)}× normal`,
body: `${agent} · ${usd(rate)}/h`,
priority: 'high',
replaceKey: 'ai-spend',
actions: [
{ kind: 'callback', id: 'pause', label: 'Pause agent',
callbackUrl: `${GATEWAY}/agents/${agent}/pause?key=${KEY}` },
{ kind: 'callback', id: 'ignore', label: 'Let it run',
callbackUrl: `${GATEWAY}/agents/${agent}/ignore?key=${KEY}` },
],
});
}AI answers most tickets. A person decides the rest.
An AI assistant can answer “where is my card?” all day, but a $480 refund for an angry customer shouldn’t be its call, and a handoff queue nobody watches leaves that customer waiting for hours.
- Routine tickets are solved without anyone being notified.
- Anything over the assistant’s limit reaches a person in seconds, with Refund and Decline on it.
- Answer the customer from the notification. The reply goes straight to their chat.
- Six customers are about to write in.
// Your assistant's handoff, when a refund is over its limit
await notify({
title: 'Refund over the AI’s limit',
body: `${customer.name} · ${usd(amount)} · ${reason}`,
priority: 'high',
url: ticket.url,
actions: [
{ kind: 'callback', id: 'refund', label: 'Refund',
callbackUrl: `${API}/tickets/${ticket.id}/refund?key=${KEY}` },
{ kind: 'callback', id: 'decline', label: 'Decline', destructive: true,
callbackUrl: `${API}/tickets/${ticket.id}/decline?key=${KEY}` },
{ kind: 'reply', id: 'answer', label: `Answer ${customer.first}`,
callbackUrl: `${API}/tickets/${ticket.id}/reply?key=${KEY}` },
],
});A warm freezer wakes someone up. A blip doesn’t.
Cheap wireless sensors are everywhere now, and they all send email. A freezer door left open at closing can cost thousands in stock by morning, while a phone on silent sleeps through it.
- Opening the door to restock never alerts. Five minutes above the limit does.
- A critical alert rings through silent mode, and keeps ringing until someone answers.
- Back to temperature, the recovery replaces the alert with how long it was warm.
for 5 minutes
Drag the temperature, or leave the door open.
# Runs on every reading from the sensor hub
if warm_for_minutes(sensor) >= 5:
requests.post(f"{PUSHR_URL}/notify",
headers={"Authorization": f"Bearer {PUSHR_TOKEN}"},
json={
"title": "Walk-in freezer 2 is warming",
"body": f"{temp:.1f}°C · door sensor: {door}",
"priority": "high",
"critical": True,
"ack": {"timeoutSec": 120, "maxAttempts": 5},
"replaceKey": "freezer-2",
})The same pieces, whatever your team runs.
A POST from any language, script or webhook. No SDK required, nothing to install on your servers.
Owners, editors and viewers per app. Viewers see everything but can’t approve.
Uptime checks, heartbeats, on-call and status pages without another tool to pay for.
The backend is open source. Self-host it when your data has to stay with you.
Wire it up tonight.
Free to start. 1,000 pushes a month and one source app are on us.
iPhone app
Feed, Live Activities, widgets and per-app sounds.
CLI
Send from the shell, pipe stdin, drive Live Activities.
SDK
Typed notify() and liveActivity() for any fetch runtime.