Custom Webhooks
Send backup event notifications to any HTTP endpoint — build custom integrations with your own monitoring systems, ITSM tools, or notification pipelines.
Creating a Custom Webhook
- Go to Monitoring → Channels → Add Channel → Custom Webhook
- Enter a Name and URL
- Select the HTTP Method (POST or PUT)
- Add any custom Headers (e.g.,
Authorization: Bearer my-secret-token) - Select which Events to send
- (Optional) Enter a Secret for signature verification
- Click Test to verify
- Click Save
Webhook Payload
All events use the same payload structure:
{
"id": "evt-a1b2c3d4",
"type": "backup.failed",
"organization_id": "org-xyz",
"organization_name": "Acme Corp",
"created_at": "2024-01-15T02:00:01Z",
"data": { ... }
}
backup.failed
{
"id": "evt-a1b2c3d4",
"type": "backup.failed",
"created_at": "2024-01-15T02:00:01Z",
"data": {
"backup_id": "bkp-a1b2c3",
"schedule_id": "sch-xyz",
"schedule_name": "postgres-prod-daily",
"agent_id": "agent-abc",
"agent_name": "prod-db-server-01",
"error": "pg_dump: error: connection to server failed",
"duration_seconds": 12,
"dashboard_url": "https://app.jokowipe.id/backups/bkp-a1b2c3"
}
}
backup.success
{
"id": "evt-b2c3d4e5",
"type": "backup.success",
"created_at": "2024-01-15T02:00:48Z",
"data": {
"backup_id": "bkp-d4e5f6",
"schedule_id": "sch-xyz",
"agent_id": "agent-abc",
"file_size_bytes": 35716096,
"duration_seconds": 47,
"storage_path": "jokowipe/2024/01/15/backup-020001.dump",
"checksum_sha256": "abc123...",
"dashboard_url": "https://app.jokowipe.id/backups/bkp-d4e5f6"
}
}
anomaly.detected
{
"id": "evt-c3d4e5f6",
"type": "anomaly.detected",
"created_at": "2024-01-15T02:01:00Z",
"data": {
"anomaly_id": "anom-a1b2c3",
"type": "size_anomaly",
"severity": "critical",
"schedule_id": "sch-xyz",
"schedule_name": "postgres-prod-daily",
"title": "Backup size dropped 78% below baseline",
"ai_analysis": "This dramatic size reduction may indicate...",
"current_value": 7.8,
"baseline_mean": 35.2,
"dashboard_url": "https://app.jokowipe.id/anomalies/anom-a1b2c3"
}
}
Signature Verification
When you configure a webhook secret, jokowipe.id signs each request with HMAC-SHA256:
X-Jokowipe-Signature: sha256=abc123def456...
X-Jokowipe-Event: backup.failed
X-Jokowipe-Delivery-ID: evt-a1b2c3d4
Verify the signature in your handler:
import hmac
import hashlib
def verify_signature(secret: str, body: bytes, signature: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
# In your Flask/FastAPI handler:
@app.post("/webhook")
def handle_webhook(request: Request):
body = request.body()
sig = request.headers.get("X-Jokowipe-Signature")
if not verify_signature(WEBHOOK_SECRET, body, sig):
raise HTTPException(status_code=401, detail="Invalid signature")
# Process the event...
Retry Policy
jokowipe.id retries failed deliveries (non-2xx responses) with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 5 minutes |
| 3 | 30 minutes |
| 4 | 2 hours |
| 5 | 6 hours |
Your endpoint should return a 2xx response within 10 seconds. For long-running processing, return 200 immediately and process the event asynchronously.
Example: Sending to Discord
Discord supports webhooks — use the Discord webhook URL as the target:
# In the webhook URL field:
https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN
# Add header:
Content-Type: application/json
Note: Discord expects a different payload format. Use a middleware service like n8n or Zapier to transform the jokowipe.id payload to Discord's format.