Skip to main content

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​

  1. Go to Monitoring → Channels → Add Channel → Custom Webhook
  2. Enter a Name and URL
  3. Select the HTTP Method (POST or PUT)
  4. Add any custom Headers (e.g., Authorization: Bearer my-secret-token)
  5. Select which Events to send
  6. (Optional) Enter a Secret for signature verification
  7. Click Test to verify
  8. 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:

AttemptDelay
1Immediate
25 minutes
330 minutes
42 hours
56 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.