Pingwire API

Send a message, alert, or reminder in one HTTP call

One endpoint. One Bearer token. Messages reach humans in a real-time chat UI and on their phone via web push — from a server, website, CLI, cron job, or plugin.

Quick answer: POST to https://pingwire.dev/api/v1/messages.php with an Authorization: Bearer <api_key> header and a JSON body like {"channel":"deploys","text":"Build #123 passed"}. You get a 201 and the message appears instantly everywhere.
Tip: Create a free account and your real API key will be injected into these examples automatically.

Quickstart — your first message in 30 seconds

Pick your language. Every example sends "Build #123 passed" to a channel called deploys (auto-created on first send).

curl -X POST https://pingwire.dev/api/v1/messages.php \
  -H "Authorization: Bearer pw_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"channel":"deploys","text":"Build #123 passed","priority":"high"}'
#!/usr/bin/env bash
PINGWIRE_KEY="pw_live_xxxxxxxxxxxxxxxxxxxxxxxx"
curl -sS -X POST https://pingwire.dev/api/v1/messages.php \
  -H "Authorization: Bearer $PINGWIRE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"channel":"deploys","text":"Build #123 passed"}'
import requests

requests.post(
    "https://pingwire.dev/api/v1/messages.php",
    headers={"Authorization": "Bearer pw_live_xxxxxxxxxxxxxxxxxxxxxxxx"},
    json={"channel": "deploys", "text": "Build #123 passed", "priority": "high"},
)
await fetch("https://pingwire.dev/api/v1/messages.php", {
  method: "POST",
  headers: {
    "Authorization": "Bearer pw_live_xxxxxxxxxxxxxxxxxxxxxxxx",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ channel: "deploys", text: "Build #123 passed" }),
});
<?php
$ch = curl_init("https://pingwire.dev/api/v1/messages.php");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer pw_live_xxxxxxxxxxxxxxxxxxxxxxxx",
    "Content-Type: application/json",
  ],
  CURLOPT_POSTFIELDS => json_encode(["channel" => "deploys", "text" => "Build #123 passed"]),
]);
curl_exec($ch);

The send endpoint

POST/api/v1/messages.phpSend a message (the core endpoint)
POST/api/v1/notify.phpIdentical alias for one-liners
GET/api/v1/messages.php?channel=deploysList recent messages (paginated)

How do I send to a person instead of a channel?

Use to with a username or a conversation UUID instead of channel:

curl -X POST https://pingwire.dev/api/v1/messages.php \
  -H "Authorization: Bearer pw_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{"to":"alice","text":"Your report is ready"}'
# Whole request body becomes the message. Channel rides in the query string.
curl -X POST "https://pingwire.dev/api/v1/notify.php?channel=alerts" \
  -H "Authorization: Bearer pw_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: text/plain" \
  --data "Disk usage at 92% on web-01"

Message fields

FieldTypeDescription
channelstringTarget channel slug. Auto-created if new. Use this or to.
tostringUsername or conversation UUID for a direct message.
textstringThe message body. Up to 16 KB.
titlestringOptional bold headline (great for alerts).
priorityenummin · low · default · high · urgent
tagsstringComma-separated labels, e.g. ci,rocket.
urlstringClick-through URL shown as a button and opened from push.

Priority levels

Priority controls how loudly a message arrives. urgent pushes with require-interaction so the notification stays until acknowledged.

min low default high urgent

Incoming webhooks — for GitHub, CI & monitors

Create a webhook for a channel in Account → Webhooks. Then POST JSON, form data, or raw text to its URL. No auth header needed — the token is the secret. Pingwire maps common fields (text, message, content, body, title) automatically.

curl -X POST https://pingwire.dev/hook/your_webhook_token \
  -H "Content-Type: application/json" \
  -d '{"title":"Deploy","text":"v1.2.3 shipped to prod","priority":"high"}'
echo "Nightly backup complete" | \
  curl -X POST https://pingwire.dev/hook/your_webhook_token \
  -H "Content-Type: text/plain" --data-binary @-

The pingwire CLI

Install once, then send from any shell, cron job, or pipeline.

# After deploy, symlink the wrapper:
chmod +x cli/pingwire
sudo ln -s /home/pingwire.dev/public_html/cli/pingwire /usr/local/bin/pingwire

# Configure (or set PINGWIRE_API_KEY in your env):
echo 'PINGWIRE_API_KEY=pw_live_xxxxxxxxxxxxxxxxxxxxxxxx' >> ~/.pingwirerc
pingwire send --channel deploys "Build #123 passed"
pingwire send --channel deploys < build.log        # pipe stdin
pingwire send --to alice --title "FYI" "ping"
pingwire remind "+10 minutes" "stand up" --channel team
pingwire remind --cron "0 9 * * 1-5" --channel team "Standup"
pingwire channels
pingwire whoami

Scheduled & recurring reminders

POST/api/v1/reminders.phpCreate a one-off or recurring reminder
GET/api/v1/reminders.phpList your reminders
DELETE/api/v1/reminders.php?id=NCancel a reminder
curl -X POST https://pingwire.dev/api/v1/reminders.php \
  -H "Authorization: Bearer pw_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{"channel":"team","text":"Release window","run_at":"2026-06-01T15:00:00Z"}'
curl -X POST https://pingwire.dev/api/v1/reminders.php \
  -H "Authorization: Bearer pw_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{"channel":"team","text":"Standup","cron":"0 9 * * 1-5","timezone":"America/New_York"}'

Requires a key with the schedule scope. Recurring schedules use standard cron expressions.

Ready-to-use recipes

GitHub Actions — notify on deploy

# .github/workflows/notify.yml
name: Notify Pingwire
on: [deployment_status]
jobs:
  ping:
    runs-on: ubuntu-latest
    steps:
      - name: Send Pingwire alert
        run: |
          curl -X POST https://pingwire.dev/hook/${{ secrets.PINGWIRE_HOOK }} \
            -H "Content-Type: application/json" \
            -d '{"title":"Deploy","text":"${{ github.repository }} deployed ${{ github.sha }}"}'

Cron server alert — disk space watchdog

# Alert if root disk usage exceeds 90%. Runs every 5 minutes.
*/5 * * * * [ $(df / | awk 'NR==2{print +$5}') -gt 90 ] && \
  pingwire send --channel alerts --priority urgent "Disk >90% on $(hostname)"

"Is my site up?" monitor

#!/usr/bin/env bash
URL="https://example.com"
if ! curl -fsS --max-time 10 "$URL" >/dev/null; then
  curl -X POST https://pingwire.dev/api/v1/messages.php \
    -H "Authorization: Bearer pw_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
    -d "{\"channel\":\"alerts\",\"title\":\"DOWN\",\"text\":\"$URL is unreachable\",\"priority\":\"urgent\"}"
fi

Minimal WordPress plugin

<?php
/* Plugin Name: Pingwire Notifier */
add_action('publish_post', function ($id) {
  $post = get_post($id);
  wp_remote_post('https://pingwire.dev/api/v1/messages.php', [
    'headers' => ['Authorization' => 'Bearer pw_live_xxxxxxxxxxxxxxxxxxxxxxxx', 'Content-Type' => 'application/json'],
    'body'    => wp_json_encode([
      'channel' => 'blog',
      'title'   => 'New post published',
      'text'    => $post->post_title,
      'url'     => get_permalink($id),
    ]),
  ]);
});

Scopes & rate limits

ScopeAllows
sendSend messages
scheduleCreate reminders
readList messages & channels, identify
adminAccount-wide automation (admins only)
  • Sends: 120 requests/min per key.
  • Webhooks: 60 requests/min per token.
  • Reminders: 60 creates/min per key.
  • Exceeding a limit returns 429 with a Retry-After header.

Errors

Errors are JSON with a stable error code and the right HTTP status.

StatusCodeMeaning
401invalid_api_keyMissing/invalid/revoked key
403insufficient_scopeKey lacks the required scope
402payment_requiredThe account needs Supporter Access — manage your plan at /account.php#plan
403forbidden_channelNot a member of the target channel
404user_not_foundto doesn't match any username (human-readable detail included)
422missing_targetNo channel or to provided
429rate_limitedSlow down — see Retry-After

llms.txt documents this API for AI coding assistants too.