Custom Webhooks
Send alerts to beeps from any system using HTTP POST requests.
Webhook Format
Section titled “Webhook Format”beeps relays accept webhook POST requests at:
https://hooks.beeps.dev/YOUR_WEBHOOK_IDBasic Example
Section titled “Basic Example”Send a simple alert:
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" }'Payload Structure
Section titled “Payload Structure”Required Fields
Section titled “Required Fields”title(string): Short alert title (max 255 characters)
Optional Fields
Section titled “Optional Fields”message(string): Detailed alert descriptionseverity(string): Alert severity levellow/infomedium/warninghigh/errorcritical/fatal
source(string): System generating the alerttimestamp(string): ISO 8601 timestamp (defaults to current time)metadata(object): Additional key-value pairs
Complete Example
Section titled “Complete Example”{ "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" }}Language Examples
Section titled “Language Examples”Python
Section titled “Python”import requestsfrom 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")Node.js
Section titled “Node.js”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"Common Patterns
Section titled “Common Patterns”Cron Job Monitoring
Section titled “Cron Job Monitoring”#!/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_codefiHealth Check Monitoring
Section titled “Health Check Monitoring”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);SSL Certificate Expiration Monitoring
Section titled “SSL Certificate Expiration Monitoring”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:
# Install dependenciesnpm install
# Run the monitornpx tsx ssl-monitor.ts
# Or run as a cron job (daily at 9 AM)0 9 * * * cd /path/to/monitor && npx tsx ssl-monitor.tsResponse Format
Section titled “Response Format”Successful webhook submission returns:
{ "success": true, "alert_id": "alr_abc123", "received_at": "2024-10-27T10:30:00Z"}Error Handling
Section titled “Error Handling”HTTP Status Codes
Section titled “HTTP Status Codes”200 OK: Alert received successfully400 Bad Request: Invalid payload format401 Unauthorized: Invalid webhook ID429 Too Many Requests: Rate limit exceeded500 Internal Server Error: Server error
Example Error Response
Section titled “Example Error Response”{ "success": false, "error": "Invalid payload: 'title' is required", "code": "INVALID_PAYLOAD"}Best Practices
Section titled “Best Practices”- Include Context: Add relevant metadata for triage
- Use Severity: Properly categorize alert urgency
- Add Timestamps: Include when issue occurred
- Link to Resources: Add dashboard/runbook URLs
- Handle Errors: Implement retry logic with backoff
- Rate Limit: Batch alerts when possible
- Validate Payloads: Check data before sending
- Use HTTPS: Always use secure connections
Rate Limits
Section titled “Rate Limits”- Default: 100 requests per minute per webhook
- Burst: Up to 10 requests per second
- Exceeding limits returns
429 Too Many Requests
What happens next
Section titled “What happens next”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.
Next Steps
Section titled “Next Steps”- Configure Relay Rules to route alerts to humans and AI agents
- Create Schedules to define on-call rotations
- Add Contact Methods for email and SMS notifications