Skip to content

Custom Webhooks

Send alerts to beeps from any system using HTTP POST requests.

beeps relays accept webhook POST requests at:

https://hooks.beeps.dev/YOUR_WEBHOOK_ID

Send a simple alert:

Terminal window
curl -X POST https://hooks.beeps.dev/YOUR_WEBHOOK_ID \
-H "Content-Type: application/json" \
-d '{
"title": "Database Connection Failed",
"message": "Unable to connect to production database",
"severity": "critical"
}'
  • title (string): Short alert title (max 255 characters)
  • message (string): Detailed alert description
  • severity (string): Alert severity level
    • low / info
    • medium / warning
    • high / error
    • critical / fatal
  • source (string): System generating the alert
  • timestamp (string): ISO 8601 timestamp (defaults to current time)
  • metadata (object): Additional key-value pairs
{
"title": "High API Error Rate",
"message": "Production API error rate exceeded 5% threshold. Current rate: 8.2%",
"severity": "critical",
"source": "monitoring-system",
"timestamp": "2024-10-27T10:30:00Z",
"metadata": {
"environment": "production",
"service": "api",
"region": "us-east-1",
"error_rate": 8.2,
"threshold": 5.0,
"dashboard_url": "https://dashboard.example.com/api",
"runbook_url": "https://wiki.example.com/runbooks/high-error-rate"
}
}
import requests
from datetime import datetime
def send_alert(title, message, severity="high"):
webhook_url = "https://hooks.beeps.dev/YOUR_WEBHOOK_ID"
payload = {
"title": title,
"message": message,
"severity": severity,
"source": "python-app",
"timestamp": datetime.utcnow().isoformat() + "Z",
"metadata": {
"host": "app-server-01",
"pid": 12345
}
}
response = requests.post(webhook_url, json=payload)
response.raise_for_status()
return response.json()
send_alert(
"Database Connection Failed",
"Could not connect to PostgreSQL after 3 retries",
severity="critical"
)
async function sendAlert(title, message, severity = "high") {
const webhookUrl = "https://hooks.beeps.dev/YOUR_WEBHOOK_ID";
const payload = {
title,
message,
severity,
source: "nodejs-app",
timestamp: new Date().toISOString(),
metadata: {
host: process.env.HOSTNAME,
pid: process.pid,
},
};
const response = await fetch(webhookUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(`Failed to send alert: ${response.statusText}`);
}
return response.json();
}
await sendAlert(
"High Memory Usage",
"Application memory usage exceeded 90%",
"warning",
);
package main
import (
"bytes"
"encoding/json"
"net/http"
"time"
)
type Alert struct {
Title string `json:"title"`
Message string `json:"message"`
Severity string `json:"severity"`
Source string `json:"source"`
Timestamp string `json:"timestamp"`
Metadata map[string]interface{} `json:"metadata"`
}
func sendAlert(title, message, severity string) error {
webhookURL := "https://hooks.beeps.dev/YOUR_WEBHOOK_ID"
alert := Alert{
Title: title,
Message: message,
Severity: severity,
Source: "go-app",
Timestamp: time.Now().UTC().Format(time.RFC3339),
Metadata: map[string]interface{}{
"host": "app-server-01",
"version": "1.2.3",
},
}
payload, err := json.Marshal(alert)
if err != nil {
return err
}
resp, err := http.Post(webhookURL, "application/json", bytes.NewBuffer(payload))
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
func main() {
sendAlert(
"Service Unavailable",
"Health check failed for payment service",
"critical",
)
}
require 'net/http'
require 'json'
require 'uri'
def send_alert(title, message, severity: 'high')
webhook_url = URI('https://hooks.beeps.dev/YOUR_WEBHOOK_ID')
payload = {
title: title,
message: message,
severity: severity,
source: 'ruby-app',
timestamp: Time.now.utc.iso8601,
metadata: {
host: `hostname`.strip,
ruby_version: RUBY_VERSION
}
}
http = Net::HTTP.new(webhook_url.host, webhook_url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(webhook_url)
request['Content-Type'] = 'application/json'
request.body = payload.to_json
response = http.request(request)
raise "Failed: #{response.code}" unless response.is_a?(Net::HTTPSuccess)
JSON.parse(response.body)
end
send_alert(
'Cache Miss Rate High',
'Redis cache miss rate exceeded 50%',
severity: 'warning'
)
#!/bin/bash
WEBHOOK_URL="https://hooks.beeps.dev/YOUR_WEBHOOK_ID"
send_alert() {
local title="$1"
local message="$2"
local severity="${3:-high}"
curl -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d @- <<EOF
{
"title": "$title",
"message": "$message",
"severity": "$severity",
"source": "bash-script",
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"metadata": {
"host": "$(hostname)",
"user": "$USER",
"script": "$0"
}
}
EOF
}
send_alert \
"Backup Failed" \
"Nightly database backup failed with exit code 1" \
"critical"
#!/bin/bash
WEBHOOK_URL="https://hooks.beeps.dev/YOUR_WEBHOOK_ID"
JOB_NAME="database-backup"
run_job() {
# Run your job
/usr/local/bin/backup-database.sh
return $?
}
notify_failure() {
curl -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d @- <<EOF
{
"title": "Cron Job Failed: $JOB_NAME",
"message": "Job failed with exit code $1",
"severity": "critical",
"source": "cron",
"metadata": {
"job": "$JOB_NAME",
"exit_code": $1,
"host": "$(hostname)",
"time": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
}
}
EOF
}
if run_job; then
echo "Job succeeded"
else
exit_code=$?
notify_failure $exit_code
exit $exit_code
fi
const WEBHOOK_URL = "https://hooks.beeps.dev/YOUR_WEBHOOK_ID";
const SERVICES = [
{ name: "api", url: "https://api.example.com/health" },
{ name: "database", url: "https://db.example.com/ping" },
{ name: "cache", url: "https://cache.example.com/status" },
];
async function checkHealth() {
for (const service of SERVICES) {
try {
const response = await fetch(service.url, { timeout: 5000 });
if (!response.ok) {
await sendAlert({
title: `${service.name} health check failed`,
message: `HTTP ${response.status}: ${response.statusText}`,
severity: "critical",
source: "health-check",
metadata: {
service: service.name,
url: service.url,
status_code: response.status,
},
});
}
} catch (error) {
await sendAlert({
title: `${service.name} unreachable`,
message: error.message,
severity: "critical",
source: "health-check",
metadata: {
service: service.name,
url: service.url,
error: error.message,
},
});
}
}
}
async function sendAlert(payload) {
await fetch(WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
...payload,
timestamp: new Date().toISOString(),
}),
});
}
setInterval(checkHealth, 60000);
import * as tls from "tls";
import * as https from "https";
const WEBHOOK_URL = "https://hooks.beeps.dev/YOUR_WEBHOOK_ID";
const WARNING_DAYS = 30;
const DOMAINS = [
"api.example.com",
"www.example.com",
"app.example.com",
];
interface CertificateInfo {
domain: string;
validTo: Date;
daysRemaining: number;
issuer: string;
subject: string;
}
async function checkCertificate(domain: string): Promise<CertificateInfo> {
return new Promise((resolve, reject) => {
const options = {
host: domain,
port: 443,
method: "GET",
rejectUnauthorized: false,
agent: false,
};
const req = https.request(options, (res) => {
const cert = res.socket.getPeerCertificate();
if (!cert || Object.keys(cert).length === 0) {
reject(new Error("No certificate found"));
return;
}
const validTo = new Date(cert.valid_to);
const now = new Date();
const daysRemaining = Math.floor(
(validTo.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)
);
resolve({
domain,
validTo,
daysRemaining,
issuer: cert.issuer.O || "Unknown",
subject: cert.subject.CN || domain,
});
});
req.on("error", reject);
req.end();
});
}
async function sendAlert(cert: CertificateInfo) {
const severity = cert.daysRemaining <= 7 ? "critical" : "warning";
const payload = {
title: `SSL Certificate Expiring Soon: ${cert.domain}`,
message: `SSL certificate for ${cert.domain} expires in ${cert.daysRemaining} days (${cert.validTo.toISOString()})`,
severity,
source: "ssl-monitor",
timestamp: new Date().toISOString(),
metadata: {
domain: cert.domain,
days_remaining: cert.daysRemaining,
expiration_date: cert.validTo.toISOString(),
issuer: cert.issuer,
subject: cert.subject,
},
};
try {
const response = await fetch(WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (!response.ok) {
console.error(`Failed to send alert: ${response.statusText}`);
} else {
console.log(`Alert sent for ${cert.domain}`);
}
} catch (error) {
console.error(`Error sending alert:`, error);
}
}
async function checkAllCertificates() {
console.log(`Checking ${DOMAINS.length} certificates...`);
for (const domain of DOMAINS) {
try {
const cert = await checkCertificate(domain);
console.log(
`${domain}: ${cert.daysRemaining} days remaining (expires ${cert.validTo.toLocaleDateString()})`
);
if (cert.daysRemaining <= WARNING_DAYS) {
await sendAlert(cert);
}
} catch (error) {
console.error(`Error checking ${domain}:`, error);
await fetch(WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
title: `SSL Certificate Check Failed: ${domain}`,
message: `Unable to retrieve certificate: ${error.message}`,
severity: "high",
source: "ssl-monitor",
timestamp: new Date().toISOString(),
metadata: {
domain,
error: error.message,
},
}),
});
}
}
}
checkAllCertificates();
setInterval(checkAllCertificates, 24 * 60 * 60 * 1000);

Usage:

Terminal window
# Install dependencies
npm install
# Run the monitor
npx tsx ssl-monitor.ts
# Or run as a cron job (daily at 9 AM)
0 9 * * * cd /path/to/monitor && npx tsx ssl-monitor.ts

Successful webhook submission returns:

{
"success": true,
"alert_id": "alr_abc123",
"received_at": "2024-10-27T10:30:00Z"
}
  • 200 OK: Alert received successfully
  • 400 Bad Request: Invalid payload format
  • 401 Unauthorized: Invalid webhook ID
  • 429 Too Many Requests: Rate limit exceeded
  • 500 Internal Server Error: Server error
{
"success": false,
"error": "Invalid payload: 'title' is required",
"code": "INVALID_PAYLOAD"
}
  1. Include Context: Add relevant metadata for triage
  2. Use Severity: Properly categorize alert urgency
  3. Add Timestamps: Include when issue occurred
  4. Link to Resources: Add dashboard/runbook URLs
  5. Handle Errors: Implement retry logic with backoff
  6. Rate Limit: Batch alerts when possible
  7. Validate Payloads: Check data before sending
  8. Use HTTPS: Always use secure connections
  • Default: 100 requests per minute per webhook
  • Burst: Up to 10 requests per second
  • Exceeding limits returns 429 Too Many Requests

Once beeps receives a POST, it creates an alert and runs your relay rules. The response includes the alert_id so you can track it programmatically.