home / skills / rohunvora / cool-claude-skills / webhook-notify

webhook-notify skill

/skills/webhook-notify

This skill sets up secure webhook to desktop notifications using Cloudflare Workers and ntfy.sh, delivering real-time alerts with custom sounds and actions.

npx playbooks add skill rohunvora/cool-claude-skills --skill webhook-notify

Review the files below or copy the command above to add this skill to your agents.

Files (7)
SKILL.md
8.5 KB
---
name: webhook-notify
description: Set up webhook-to-desktop notification systems using Cloudflare Workers and ntfy.sh. Use when users want to receive desktop notifications for webhooks from services like Stripe payments, GitHub events, custom app alerts, error monitoring, or any webhook source. Handles the full setup including worker deployment, local listener, and background service installation. Supports custom sounds, click actions, rich content, and event filtering.
---

# Webhook Desktop Notifications

Set up secure, reliable webhook-to-desktop notification systems that receive webhooks from any source and display native desktop notifications on macOS.

## Architecture

The system uses a proven pattern:

1. **Cloudflare Worker** - Receives webhooks from external services (Stripe, GitHub, custom apps)
2. **ntfy.sh** - Message relay service (no account required)
3. **Local Listener** - Background process that subscribes to ntfy.sh and displays desktop notifications
4. **macOS Service** - Keeps listener running automatically

**Benefits:**
- No local port exposure or tunneling required
- Secure and reliable message delivery
- Free tier sufficient for most use cases
- Handles network interruptions with automatic reconnection

## Quick Start

Run the init script to set up a new webhook notification system:

```bash
scripts/init_webhook.sh <webhook-name> <ntfy-topic> [output-dir] [sound-file]
```

**Example:**
```bash
scripts/init_webhook.sh stripe-payments myapp-stripe
```

This creates:
- Cloudflare Worker for receiving webhooks
- Local listener script for desktop notifications
- macOS launchd service configuration
- All necessary configuration files

## Workflow

### Step 1: Initialize Webhook System

Run `init_webhook.sh` with appropriate parameters:

**Arguments:**
- `webhook-name`: Identifier for this webhook (e.g., "stripe-payments", "github-prs")
- `ntfy-topic`: Your unique ntfy.sh topic name (e.g., "myapp-notifications-2024")
- `output-dir`: (Optional) Where to create files (default: `~/.claude/webhooks/<webhook-name>`)
- `sound-file`: (Optional) Custom notification sound (default: system sound)

**Example with custom sound:**
```bash
scripts/init_webhook.sh github-alerts my-github-topic ~/webhooks /path/to/alert.mp3
```

### Step 2: Customize Worker Logic

Edit the generated `worker.js` to handle your specific webhook format.

The worker template includes examples for:
- Generic webhooks (title/message/url format)
- Stripe payment events
- GitHub events (PRs, issues, releases)

**Modify the `extractNotification()` function** to parse your webhook structure and return notification details.

**Example for custom app webhooks:**
```javascript
function extractNotification(event) {
  // Your custom webhook format
  if (event.type === 'user.signup') {
    return {
      title: 'New User Signup',
      message: `${event.user_email} just created an account`,
      priority: '4',
      tags: 'user,tada',
      url: `https://myapp.com/admin/users/${event.user_id}`,
    };
  }

  return null; // Ignore other events
}
```

**Notification object fields:**
- `title`: Notification title (required)
- `message`: Notification body text (required)
- `priority`: 1-5, where 5 is highest (optional, default: 3)
- `tags`: Emoji tags for ntfy.sh like "moneybag", "warning", "fire" (optional)
- `url`: URL to open when notification is clicked (optional)

**For common patterns**, see [EXAMPLES.md](references/EXAMPLES.md) with complete examples for:
- Stripe payments and refunds
- GitHub PR/issue notifications
- Error monitoring and alerts
- Custom business events
- Event filtering and routing

### Step 3: Deploy Cloudflare Worker

Navigate to the webhook directory and deploy:

```bash
cd ~/.claude/webhooks/<webhook-name>/webhook
npx wrangler deploy
```

The deployment will output your worker URL:
```
https://<webhook-name>.<your-subdomain>.workers.dev
```

**Configure your webhook source** to send POST requests to:
```
https://<webhook-name>.<your-subdomain>.workers.dev/webhook
```

**Optional: Add webhook secret** for signature verification:
```bash
npx wrangler secret put WEBHOOK_SECRET
```

### Step 4: Install Local Listener

Install the listener as a background service that starts automatically:

```bash
scripts/install_service.sh ~/.claude/webhooks/<webhook-name>/com.webhook.<webhook-name>.plist
```

The service will:
- Start immediately
- Restart automatically if it crashes
- Run on system startup

**Test manually before installing:**
```bash
~/.claude/webhooks/<webhook-name>/listener.sh
```

Then send a test notification:
```bash
curl -d 'Test notification!' https://ntfy.sh/<your-topic>
```

### Step 5: Test End-to-End

Send a test webhook to verify the complete flow:

```bash
curl -X POST https://<webhook-name>.<your-subdomain>.workers.dev/webhook \
  -H "Content-Type: application/json" \
  -d '{"title":"Test","message":"Hello from webhook!"}'
```

You should see:
1. Worker logs in Cloudflare dashboard
2. Message in listener logs: `~/.claude/webhooks/<webhook-name>/logs/listener.log`
3. Desktop notification with sound

## Troubleshooting

**No notification received:**
1. Check worker logs: `cd webhook && npx wrangler tail`
2. Check listener logs: `tail -f ~/.claude/webhooks/<webhook-name>/logs/*.log`
3. Verify service is running: `launchctl list | grep webhook`
4. Test ntfy.sh directly: `curl -d "Test" https://ntfy.sh/<your-topic>`

**Notification sound not playing:**
- Verify sound file exists and is valid audio format
- Check macOS notification permissions for `terminal-notifier`
- Try with default system sound first

**Service keeps stopping:**
- Check stderr log: `cat ~/.claude/webhooks/<webhook-name>/logs/stderr.log`
- Ensure `terminal-notifier` is installed: `brew install terminal-notifier`
- Verify listener script has execute permissions

**Too many notifications (keepalive messages):**
- The listener template already filters empty keepalive messages
- If still seeing duplicates, check worker `extractNotification()` logic

## Common Patterns

### Multiple Webhooks

Create separate notification systems for different sources:

```bash
scripts/init_webhook.sh stripe-payments myapp-stripe
scripts/init_webhook.sh github-alerts myapp-github
scripts/init_webhook.sh error-monitoring myapp-errors
```

Each runs independently with its own worker, listener, and service.

### Event Filtering

Return `null` from `extractNotification()` to ignore events:

```javascript
function extractNotification(event) {
  // Ignore test events
  if (event.test || event.environment !== 'production') {
    return null;
  }

  // Ignore low-priority actions
  if (event.action === 'labeled') {
    return null;
  }

  // Handle other events...
}
```

### Priority-Based Sounds

Modify `listener.sh` to play different sounds by priority:

```bash
priority=$(echo "$message" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('priority','3'))" 2>/dev/null || echo "3")

if [[ "$priority" == "5" ]]; then
    afplay "/System/Library/Sounds/Sosumi.aiff" &  # Critical
elif [[ "$priority" == "4" ]]; then
    afplay "$CUSTOM_SOUND" &  # High priority
fi
```

### Rich Notifications

ntfy.sh supports image attachments and action buttons:

```javascript
await fetch(NTFY_URL, {
  method: 'POST',
  headers: {
    'Title': notification.title,
    'Attach': 'https://example.com/screenshot.png',
    'Actions': 'view, View details, https://example.com',
  },
  body: notification.message,
});
```

## Managing Services

**Check if service is running:**
```bash
launchctl list | grep com.webhook.<webhook-name>
```

**View live logs:**
```bash
tail -f ~/.claude/webhooks/<webhook-name>/logs/*.log
```

**Restart service:**
```bash
launchctl unload ~/Library/LaunchAgents/com.webhook.<webhook-name>.plist
launchctl load ~/Library/LaunchAgents/com.webhook.<webhook-name>.plist
```

**Stop service:**
```bash
launchctl unload ~/Library/LaunchAgents/com.webhook.<webhook-name>.plist
```

**Uninstall completely:**
```bash
launchctl unload ~/Library/LaunchAgents/com.webhook.<webhook-name>.plist
rm ~/Library/LaunchAgents/com.webhook.<webhook-name>.plist
rm -rf ~/.claude/webhooks/<webhook-name>
```

## Resources

### scripts/

- `init_webhook.sh` - Initialize new webhook notification system
- `install_service.sh` - Install listener as macOS background service

### assets/

- `worker-template.js` - Cloudflare Worker template
- `listener-template.sh` - Local listener script template
- `launchd-template.plist` - macOS service configuration template

### references/

- `EXAMPLES.md` - Complete examples for common webhook sources (Stripe, GitHub, error monitoring, custom apps) with code samples and patterns

Overview

This skill sets up a webhook-to-desktop notification pipeline using Cloudflare Workers and ntfy.sh, delivering native macOS notifications for webhooks like Stripe, GitHub, error monitors, or custom apps. It automates worker creation, local listener installation, and a macOS background service so notifications arrive securely without exposing local ports. Supports custom sounds, click actions, rich content, and event filtering.

How this skill works

A Cloudflare Worker receives incoming webhooks and transforms them into ntfy.sh messages with headers for title, attachments, priority, and actions. ntfy.sh acts as the relay so the local listener only needs to subscribe to your topic. A small background listener process subscribes to ntfy.sh, parses messages, plays sounds by priority, and shows native notifications (with optional click-to-open URLs). The listener is installed as a launchd service on macOS to run automatically and restart on failure.

When to use it

  • You want desktop alerts for Stripe payments, refunds, or charge failures.
  • You need GitHub PR/issue/release notifications directly on your desktop.
  • You want local alerts for custom app events or server error monitoring.
  • You prefer no local port exposure or tunneling for webhook delivery.
  • You want actionable notifications with URLs, images, or custom sounds.

Best practices

  • Use a unique ntfy.sh topic per notification stream to avoid cross-talk.
  • Filter events in the worker (extractNotification) to drop noise before relaying.
  • Set webhook secrets and verify signatures in the worker for security.
  • Test end-to-end with curl to validate worker → ntfy → listener flow before enabling production traffic.
  • Use priority fields and separate sounds for urgent vs low-priority alerts.

Example use cases

  • Stripe payments: notify when charges succeed or fail with amount and customer email.
  • GitHub: notify on new PRs or reviews with link to the PR and author.
  • Error monitoring: send critical errors as priority=5 with stacktrace link to issue tracker.
  • Custom app: alert ops on important business events (new signup, large order) with click-to-open admin URL.
  • Multiple independent systems: run separate worker+listener pairs for different teams or apps.

FAQ

Do I need an ntfy.sh account?

No account is required; ntfy.sh works with public topics or optionally authenticated topics if you want more control.

Will this expose my machine to the internet?

No. The Cloudflare Worker receives webhooks and relays via ntfy.sh. The local listener only subscribes outbound to ntfy.sh, so no inbound ports or tunnels are required.