# Pingwire > Pingwire is a developer-first messaging and notification platform. Developers send a message, alert, or reminder into a chat in under 30 seconds — from a server, website, CLI, cron job, or plugin — using one HTTP call or one CLI command. Messages reach humans in a real-time chat UI and on their phone via web push. It runs as a managed service at pingwire.dev and is also available to self-host. Base URL: https://pingwire.dev Auth: HTTP Bearer API key — `Authorization: Bearer pw_live_...` (create keys in Account → API keys). Scopes: send, schedule, read, admin. ## Send a message (the core endpoint) POST https://pingwire.dev/api/v1/messages.php Headers: Authorization: Bearer ; Content-Type: application/json Body (JSON, form-encoded, or raw text/plain all accepted): { "channel": "deploys", // target channel slug (auto-created on first send) — OR: "to": "username", // a username or conversation UUID for a direct message "text": "Build #123 passed", "title": "CI", // optional bold headline "priority": "high", // min | low | default | high | urgent "tags": "ci,rocket", // optional comma-separated labels "url": "https://ci/123" // optional click-through URL } Response: 201 { "id", "uuid", "conversation", "priority", "created_at" } Alias: POST https://pingwire.dev/api/v1/notify.php (identical; raw text body becomes the message, with channel in the query string e.g. ?channel=alerts). Self-notification: on a single account, messages you send via the API, CLI, webhooks, or the scheduler push to your own phone once notifications are enabled on a device — no second account needed. The only messages not pushed back to you are the ones you type yourself in the chat UI (no echo). A separate "bot"/sender account is optional, useful for teams, shared channels, or a dedicated send-only identity. List messages: GET https://pingwire.dev/api/v1/messages.php?channel=deploys&limit=30 Example (curl): curl -X POST https://pingwire.dev/api/v1/messages.php \ -H "Authorization: Bearer pw_live_xxx" \ -H "Content-Type: application/json" \ -d '{"channel":"deploys","text":"Build passed","priority":"high"}' ## Incoming webhooks (no auth header) POST https://pingwire.dev/hook/{token} The URL token is the secret. Accepts JSON, form data, or raw text (auto-detected). Common fields mapped automatically: text | message | content | body, and title | subject. Ideal for GitHub, CI, and uptime monitors. Create a webhook in Account → Webhooks. ## Reminders (scheduled & recurring) POST https://pingwire.dev/api/v1/reminders.php (scope: schedule) One-off: {"channel":"team","text":"Release","run_at":"2026-06-01T15:00:00Z"} Recurring: {"channel":"team","text":"Standup","cron":"0 9 * * 1-5","timezone":"America/New_York"} GET https://pingwire.dev/api/v1/reminders.php list DELETE https://pingwire.dev/api/v1/reminders.php?id=N cancel ## Channels & identity GET https://pingwire.dev/api/v1/channels.php list channels you can post to POST https://pingwire.dev/api/v1/channels.php {"name":"Deploys"} create a channel GET https://pingwire.dev/api/v1/me.php identity for the current key (whoami) ## QR Channels (subscribe-by-scan) QR Channels let anyone subscribe to your notifications by scanning a branded QR code — no account or app required. Send one message and it pushes to every device that scanned. Overview: https://pingwire.dev/features/qr-channels Create a QR channel (REST): POST https://pingwire.dev/api/v1/channels.php with {"qr":true,"name":"Deploys","description":"...","brand_color":"#0F62FE"} → returns subscribe_url + qr_png_url + qr_svg_url. Public subscribe flow: scanning the QR opens https://pingwire.dev/s/{token} (a no-account web-push opt-in page); QR images are at https://pingwire.dev/qr/{token}.png and .svg; one-click unsubscribe at https://pingwire.dev/u/{unsub_token}. Public subscribe API (no auth, rate-limited): POST https://pingwire.dev/api/v1/public/subscribe.php {"subscribe_token":"...","webpush":{"endpoint":"...","keys":{"p256dh":"...","auth":"..."}}} → returns an unsub_token. Send to all subscribers: POST https://pingwire.dev/api/v1/messages.php {"channel":"deploys","text":"...","title":"...","priority":"high"} — fans out as web push to every active subscriber; expired endpoints (404/410) auto-deactivate. List + subscribers + QR asset: GET /api/v1/channels.php (QR channels include subscribe_url and counts), GET /api/v1/channels/{id}/subscribers, GET /api/v1/channels/{id}/qr.(png|svg). CLI: pingwire qr create --name "Deploys" | pingwire qr list | pingwire qr image --format png --out file | pingwire qr subscribers | pingwire qr send --title "..." --body "..." MCP tools (docs: https://pingwire.dev/docs/mcp.php): create_qr_channel(name, description?, brand_color?), list_qr_channels(), get_subscribe_qr(channel), send_to_channel(channel, body, title?, priority?). Plans: FREE = 1 channel, 50 subscribers/channel, no custom branding, 30-day analytics, "Powered by Pingwire" footer. PRO = unlimited channels & subscribers, custom logo/color/QR style, vanity slug, 365-day analytics, no footer. ## Message templates (named, versioned) POST https://pingwire.dev/api/v1/templates.php {"name":"Deploy done","body":"{{service}} deployed to {{env}}"} — create (version 1). Editing writes a new immutable version; older versions remain renderable. {"id":N,"action":"preview","variables":{...}} renders without saving; {"id":N,"action":"archive"} archives. GET lists your templates. Reads need scope `read`, writes `send`. ## Subscriber segments (QR channels) Segments group a QR channel's subscribers so a send can target a slice instead of everyone. GET https://pingwire.dev/api/v1/segments.php?channel=N lists segments with live member counts; POST {"channel":N,"name":"VIP"} creates; POST {"channel":N,"segment":S,"action":"add"|"remove","subscribers":[ids]} manages membership. ## Notes to self Sending to your OWN username (via `to`) routes to a private "Notes to self" conversation and pushes to your own devices — a personal reminder/notification lane with no second account. Works from the API, CLI, and the MCP connector. ## MCP connector (Claude & AI assistants) Pingwire ships a remote MCP server so AI assistants can send messages, schedule reminders, manage QR channels and subscribers, and create/read monitors and incidents as tools. Connect with a Pingwire API key, or via OAuth 2.1 (PKCE) as the claude.ai connector does. Setup: https://pingwire.dev/docs/mcp.php — `npm install -g pingwire-mcp` for the local stdio bridge. ## SDKs Thin wrappers over the same REST API for Node, Python, and PHP are being published to npm, PyPI, and Packagist (package name `pingwire` / `pingwire/sdk`). Until they land in the registries, plain HTTP (one POST) and the bundled CLI are the supported integration paths — no SDK is required. ## CLI Install: ln -s /home/pingwire.dev/public_html/cli/pingwire /usr/local/bin/pingwire Config: PINGWIRE_API_KEY and PINGWIRE_URL (env or ~/.pingwirerc) Commands: pingwire send [--channel X | --to USER] [--title T] [--priority P] [--url U] "message" pingwire send --channel deploys < build.log # read body from stdin pingwire remind "+10 minutes" "stand up" --channel team pingwire remind --cron "0 9 * * 1-5" --channel team "Standup" pingwire channels pingwire whoami ## Limits & errors Rate limits: sends 120/min/key, webhooks 60/min/token, reminders 60/min/key. Over limit → HTTP 429 with Retry-After. Dedupe: identical text from the same machine sender (api/webhook/bot) within 60 seconds collapses into one message, so a retrying script cannot spam a channel. Human, scheduled, and monitor messages are never deduped. Errors are JSON: { "error": "", ... } with the matching HTTP status (401 invalid_api_key, 402 payment_required — the account needs Supporter Access (a monthly subscription managed at pingwire.dev/account.php#plan), 403 insufficient_scope / forbidden_channel, 404 user_not_found — the "to" username doesn't exist (includes a human-readable "detail"), 422 missing_target, 429 rate_limited). ## Pages - https://pingwire.dev/ — overview - https://pingwire.dev/developers.php — full API docs with copy-paste examples (curl, Bash, Python, Node, PHP) - https://pingwire.dev/blog/ — engineering blog: guides on notifications, monitoring, web push, and alerting (new article daily; RSS at /blog/feed.xml) - https://pingwire.dev/faq.php — frequently asked questions - https://pingwire.dev/about.php — what Pingwire is and how it works - https://pingwire.dev/pingwire-vs-telegram — Pingwire vs Telegram: when to use browser web push vs a Telegram bot for notifications (fair, factual comparison) - https://pingwire.dev/uptime-monitoring — website uptime monitoring: Pingwire polls a URL on a schedule and pushes your phone on a wrong status code, missing/forbidden keyword, timeout, or an SSL certificate nearing expiry, then again on recovery - https://pingwire.dev/heartbeat-monitoring — heartbeat (dead man's switch) monitoring for cron jobs, backup scripts, background workers, and long-running AI agent runs: the job calls a unique URL on success, and silence past the expected interval plus grace opens an incident - https://pingwire.dev/traffic-alerts — traffic spike and drop alerts: a cookieless 1×1 beacon counts visits per minute, and Pingwire pushes when traffic flatlines (catching broken deploys, DNS/CDN faults and dead JS bundles that still return HTTP 200) or spikes far above a rolling baseline ## Feature list REST API · incoming webhooks · CLI · SDKs (npm, PyPI, Composer) · one-off & recurring reminders · channels and 1:1/group chat · real-time delivery with typing and read receipts · web push (PWA) · file uploads · scoped API keys · bot identities · OAuth 2.1 for integrations · MCP connector for Claude and other AI assistants · QR Channels (subscribe-by-scan) · subscriber segments · named versioned message templates · notes-to-self (message your own devices) · machine-sender dedupe · managed cloud service (also self-hostable) · uptime monitoring · heartbeat (dead man's switch) monitoring · traffic spike and drop alerts · incidents with acknowledge/resolve · escalation policies · public status pages. ## Monitoring Three monitor types feed one incident engine, and alerts are delivered as web push plus a message in the owner's Pingwire chat. - Uptime (HTTP): Pingwire requests a URL on an interval and records the status code, latency and SSL days remaining. Alerts on an unexpected status, a keyword that must be present or must be absent, a timeout, a connection error, or a certificate nearing expiry. Redirects are followed with every hop re-validated. - Heartbeat: the job calls `GET https://pingwire.dev/hb/` on success; `/hb//start` marks a run beginning, `/hb//fail` or `/hb//` reports an explicit failure and opens an incident immediately. If no check-in arrives within the configured period plus grace, an incident opens. Suited to cron jobs, backups, workers, and autonomous AI agent runs. - Traffic: a 1×1 GIF beacon (`https://pingwire.dev/t/.gif`) or a server-side POST to `https://pingwire.dev/api/v1/traffic/` counts hits into one-minute buckets. Alerts on a spike above a rolling median baseline, or a drop to near zero — a second, independent signal that catches failures which still return HTTP 200. Incidents open after two consecutive failed checks (configurable), can be acknowledged and resolved from the dashboard, and close automatically on recovery with a downtime summary. Escalation policies (POST /api/v1/escalations.php) notify an ordered chain of people or channels when an incident goes unacknowledged. Public status pages (POST /api/v1/status-pages.php) publish your monitors' current state and history at a shareable tokenized link (https://pingwire.dev/status/{token}), with a rotate action to revoke a shared link. Honest limits: checks run from a single server (one vantage point, not a multi-region probe network); alert delivery is web push and in-app chat — no SMS, phone calls, email alerts, or on-call calendar scheduling; Pingwire publishes no uptime SLA for itself. Free plan: 3 monitors, 5-minute minimum check interval, 7 days of history. Pro plan: 25 monitors, 1-minute minimum interval, 90 days of history. Privacy of the traffic beacon: no cookies and no identifiers. Request IPs are hashed with a salt that rotates daily and only the hash is stored; those hashes are deleted as soon as the minute they belong to is evaluated, leaving only counts.