PingHook Documentation
Webhooks to Telegram, Slack, and Discord โ no account, no dashboard, just POST.
Quick Start
Get your webhook URL from the bot โ Slack: type /pinghook in any channel ยท Telegram: message @PingHookBot and send /pinghook. No email, no sign-up.
Send a test ping from your terminal:
The message arrives in Telegram within a second. That's it โ paste the URL into Grafana, GitHub Actions, n8n, a cron job, or any HTTP client.
API Endpoint
One endpoint. No authentication headers โ the API key is part of the URL path.
| Field | Description |
|---|---|
| api_key | Your unique key โ get it by sending /pinghook to the bot |
| label | Optional. Any URL path after your key โ slashes become part of the label: /send/KEY/github/ci/prod. Shows in every notification. |
| body | Any payload โ JSON or plain text. Max 100 KB. Content-Type header is optional. |
Labels
The label is everything after your API key in the URL. It appears as the notification header and can target rules. Labels support slashes for hierarchy.
Simple label:
Hierarchical โ slashes are part of the label:
No label:
Payload Formats
JSON
JSON is auto-detected and pretty-printed in a monospace code block. You don't need a Content-Type header โ PingHook tries JSON parsing first on every request.
Plain Text & Markdown
Plain text is delivered as-is. Telegram renders a subset of Markdown natively.
| Syntax | Result |
|---|---|
| **bold** | Bold text |
| _italic_ | Italic text |
| ~~strikethrough~~ | Strikethrough |
| `inline code` | Monospace inline |
Responses & Errors
| HTTP | status field | Meaning |
|---|---|---|
| 200 | success | Delivered to one or more channels |
| 200 | suppressed | Filtered by a rule or dedup โ not an error |
| 200 | failed | All channel deliveries failed (downstream issue) |
| 401 | โ | Unknown API key |
| 413 | โ | Payload exceeds 100 KB |
| 429 | โ | Rate limit exceeded โ response includes resets_in (seconds) |
"status":"suppressed". Check the body if your script needs to distinguish deliveries from filtered events.
Layer 1 โ pinghook_rules Highest Priority
For sources you own. Add a pinghook_rules key to your JSON body โ PingHook evaluates it, strips it, and delivers the rest. Recipients never see it.
This delivers only when errors > 10 AND branch == "main", suppresses repeats for 30 minutes, and routes to Slack only. The delivered payload is everything minus pinghook_rules.
Use dot notation for nested fields: "field": "labels.env" or "field": "alerts.0.status".
| Operator | Description | Type |
|---|---|---|
| eq / neq | Equals / not equals | any |
| gt / lt | Greater than / less than | numeric |
| gte / lte | Greater or equal / less or equal | numeric |
| contains | String contains (case-insensitive) | string |
| exists | Field is present (any value) | any |
Shorthand keys inside pinghook_rules
| Key | Value | Description |
|---|---|---|
| logic | AND | OR | Condition logic (default: AND) |
| dedup | integer (minutes) | Suppress same label within N minutes |
| channel | slack | telegram | Route to one platform only |
Layer 2 โ Query Params Per-Request
No body changes needed. Append params to the webhook URL. Works for any source where you can customise the URL โ Grafana, Uptime Kuma, most SaaS tools.
| Param | Value | Description |
|---|---|---|
| ?channel= | slack | telegram | Route to one platform, ignore others |
| ?dedup= | N (integer, minutes) | Suppress same label within N minutes |
| ?silent= | 1 / true / yes | Log but don't deliver โ dry-run mode |
| ?if= | field:operator:value | JSON field condition. Repeatable โ AND. Fails closed on non-JSON body. |
| ?textif= | word | Body contains word (case-insensitive). Repeatable โ AND. Works on any body type. |
| ?ai= | 1 / claude / deepseek | Append an AI-generated triage card below the notification. 1 and claude use Claude Haiku; deepseek uses DeepSeek-V3. |
?if= format: field.path:operator:value โ dots for nesting, colon as separator.
Route to a specific platform only:
Suppress same-label repeats for 30 minutes:
Log but don't deliver โ dry-run:
JSON field condition โ only deliver when status equals firing:
Multiple ?if= params โ AND logic:
Text body contains (case-insensitive, repeatable โ AND):
Combine params โ condition + platform routing + dedup:
?textif= instead.
AI triage โ analyse the payload and append an interpretation card (Claude Haiku):
Use DeepSeek-V3 instead:
Layer 3 โ Global Bot Rules Fallback
Configured once via /pinghook rules in Slack or Telegram. Applies to every ping on your key โ use when you can't modify the body or the URL at all.
/pinghook rules add keyword error
Only deliver if payload contains "error" (case-insensitive)
/pinghook rules add dedup 30
Suppress same label repeated within 30 minutes
/pinghook rules add labels ci deploy
Only deliver pings whose label is "ci" or "deploy"
/pinghook rules remove 2
Remove rule #2. /pinghook rules clear confirm removes all.
Rule evaluation order
Layer 1 (pinghook_rules in body) โ Layer 2 (query params) โ Layer 3 (bot rules). Higher layers take full priority. When Layer 1 is present, the ?if= and ?textif= conditions in Layer 2 are skipped โ but ?channel= and ?dedup= still apply as defaults that Layer 1 can override.
AI Analysis
Append ?ai=1 to any webhook URL and PingHook will analyse the payload with an AI model and attach a triage card below your notification. No prompt engineering needed โ just add the param.
Providers
| Param | Model |
|---|---|
| ?ai=1 | Claude Haiku (default) |
| ?ai=claude | Claude Haiku |
| ?ai=deepseek | DeepSeek-V3 |
Output format
The AI card is appended below the original payload, separated by a divider. It always follows this structure:
kubectl get pods -n payments and tail logs on the failing replicaField reference
| Field | Description |
|---|---|
| Summary | Emoji + plain-English interpretation of what happened and its impact. Emoji signals severity at a glance: ๐ด critical, ๐ก warning, ๐ข resolved, ๐ success, ๐ alert, ๐ณ payment, ๐ deploy, ๐ stopped. |
| Severity | One of: Critical / Warning / Info / Success |
| HTTP [code] | Only present when a 4xx or 5xx status code appears in the payload. Classifies as client-side or server-side and explains the specific code. Omitted for 2xx and when no HTTP status is found. |
| Suggested Next Step | Specific, actionable steps referencing real values from the payload โ file names, service names, error types. Never generic advice. |
Behaviour notes
- The label (URL segment after your key) is used as additional context when the payload alone is insufficient.
- Timestamps in the payload (ISO 8601 or Unix epoch) are converted to relative human-readable form, e.g. "2 minutes ago".
- Payloads are truncated to 2 000 characters before being sent to the AI.
- If the AI call fails or times out, the original notification is delivered unchanged โ no ping is lost.
?ai= to each source URL where you want it. This keeps analysis intentional and lets you control costs per source.
Bot Commands
All commands use the /pinghook prefix โ identical on both Slack and Telegram. In Slack, type them in any channel or DM with the bot. In Telegram, send them to @PingHookBot.
| Command | Description |
|---|---|
| /pinghook | Create account (if new) and get your unique webhook URL |
| /pinghook start | Same as above โ explicit form |
| /pinghook mykey | Show current webhook URL |
| /pinghook regen confirm | Regenerate API key โ old key is dead immediately |
| /pinghook channels | List all active delivery channels |
| /pinghook connect slack <url> | Add a Slack incoming webhook โ validated with a test ping |
| /pinghook disconnect <n> | Remove channel #n (from /pinghook channels list) |
| /pinghook rules | List active global rules |
| /pinghook rules add keyword <word> | Only deliver if payload contains this word |
| /pinghook rules add dedup <minutes> | Suppress same-label repeats within N minutes |
| /pinghook rules add labels <l1> <l2> โฆ | Only deliver these labels (whitelist) |
| /pinghook rules remove <n> | Remove rule #n |
| /pinghook rules clear confirm | Remove all rules |
| /pinghook usage | Ping stats โ today, this week, all time, last ping |
| /pinghook history | Last 10 successfully delivered pings with payload preview |
| /pinghook replay <n> | Re-dispatch ping #n to all channels |
| /pinghook help | List all commands |
Slack Bot Setup
Install the PingHook bot into your Slack workspace to use /pinghook slash commands and receive webhook notifications directly in Slack channels.
Step 1 โ Create a Slack App
Go to api.slack.com/apps โ Create New App โ From scratch. Name it PingHook and select your workspace.
Step 2 โ Add bot permissions
In the sidebar go to OAuth & Permissions โ Bot Token Scopes.
Add these two scopes: commands and chat:write. Nothing else needed.
Step 3 โ Register the slash command
In the sidebar go to Slash Commands โ Create New Command.
Fill in the fields:
| Field | Value |
|---|---|
| Command | /pinghook |
| Request URL | https://pinghook.dev/slack/events |
| Short description | Webhook notifications via PingHook |
| Usage hint | start | mykey | rules | history | help |
Step 4 โ Install and get your tokens
Go to Install App โ Install to Workspace. Authorize it.
Copy the Bot User OAuth Token (starts with xoxb-) โ this is your SLACK_BOT_TOKEN.
Go to Basic Information โ Signing Secret โ this is your SLACK_SIGNING_SECRET.
Add both to your .env and redeploy.
Step 5 โ Test it
In any Slack channel type /pinghook. You should receive a DM from the bot with your webhook URL.
https://yourdomain.com/slack/events).
Integration โ Grafana
Grafana sends a JSON payload when an alert fires or resolves. Add ?if=status:eq:firing to only receive notifications when an alert fires โ not when it resolves.
Setup
In Grafana: Alerting โ Contact points โ New contact point
Type: Webhook
URL: https://pinghook.dev/send/YOUR_KEY/grafana?if=status:eq:firing
Save and send a test notification to confirm it works
Example Grafana payload
Useful filter combinations
Only prod alerts, only when firing:
Suppress duplicate alerts for 10 minutes:
Route critical alerts to Telegram only, everything else to Slack (set as two separate contact points in Grafana):
Integration โ GitHub Actions
Add a step at the end of any workflow. Use if: failure() to notify only on failures, or different labels per outcome.
Store your key in GitHub: Settings โ Secrets and variables โ Actions โ New repository secret โ PINGHOOK_KEY.
Different labels per outcome
Integration โ Uptime Kuma
Add PingHook as a webhook notification. Use ?if=heartbeat.status:eq:0 to notify only when a monitor goes down, not on recovery.
Setup
In Uptime Kuma: Settings โ Notifications โ Add Notification
Type: Webhook
URL: https://pinghook.dev/send/YOUR_KEY/uptime-kuma?if=heartbeat.status:eq:0
Uptime Kuma payload (example)
| heartbeat.status | Meaning |
|---|---|
| 0 | Down |
| 1 | Up / recovered |
| 2 | Pending |
| 3 | Maintenance |
Integration โ n8n
Use PingHook as a notification step in any n8n workflow via the HTTP Request node.
| Setting | Value |
|---|---|
| Method | POST |
| URL | https://pinghook.dev/send/YOUR_KEY/n8n-alert |
| Body Content Type | JSON |
| JSON Body | Any fields from your workflow data |
You can use n8n expressions in the URL for dynamic labels: https://pinghook.dev/send/KEY/{{ $json.eventType }}.
Integration โ Python
JSON payload:
Plain text:
With dedup โ suppress the same label for 30 minutes:
Minimal wrapper function
Integration โ Cron Jobs
Add one line to any shell script or crontab to get notified on failure โ or on completion.
Ping on completion (append to any script):
Ping only on failure โ dead-man's switch:
In crontab with dedup โ suppress repeated hourly alerts so only the first failure comes through:
?dedup=55 on an hourly cron means the first failure pings you, but subsequent failures are silent until the job recovers โ no alert storm.
Rate Limits
| Limit | Free tier |
|---|---|
| Per hour | 100 requests |
| Per day | 1,000 requests |
| Max payload size | 100 KB per request |
When exceeded, you get HTTP 429 with resets_in in the response body (seconds until the window resets). Rate-limited requests are still logged โ check /pinghook usage in the bot for your counts.
Suppressed pings (filtered by rules) do not count against the rate limit.
Self-Hosting
PingHook is fully open source. Run your own instance with your own bot and database.
Requirements
- Python 3.11+
- A Telegram bot token (from @BotFather)
- PostgreSQL database (Supabase free tier works)
- Public HTTPS URL (Render, Railway, Fly.io free tiers all work)
Setup
Clone and install:
Create .env:
Apply the schema โ paste schema.sql into the Supabase SQL editor and run it.
Start the server:
Register the Telegram webhook (run once after every deploy):
ngrok http 8000, set BASE_URL to the ngrok HTTPS URL, then run python webhook.py to register it with Telegram.