Skip to main content

Configure org webhooks

Wire your organization's webhook receiver so downstream systems (Slack, custom backend, monitoring) get notified when long-running operations complete or fail. Configured at Organizations → <org> → Webhooks tab. Org admin only.

What gets delivered​

Webhooks fire on every async operation the platform completes or fails for this org:

  • helm.install — marketplace package installed on an edge
  • helm.upgrade — release upgraded
  • helm.uninstall — release removed
  • vm.start / vm.stop / vm.restart / vm.migrate
  • velero.backup / velero.restore
  • edge.discover — kubeconfig-driven edge import
  • … and any future async operation

Each delivery is a JSON POST to your URL with:

{
"event": "operation.completed", // or "operation.failed"
"operation_id": 12345,
"type": "helm.install",
"status": "completed",
"org_id": "uuid-of-org",
"user_id": "uuid-of-the-user-who-triggered",
"username": "alice@example.com",
"error": "", // populated when failed
"payload": { ... }, // original request body
"result": { ... }, // execution result (completed only)
"timestamp": "2026-05-13T12:34:56Z"
}

If a Bearer token is configured on the webhook, every POST carries Authorization: Bearer <token>. Receivers should verify it before accepting the payload.

Steps​

Add a webhook​

  1. Organizations → <org> → Webhooks tab.
  2. + Add webhook.
  3. Fill:
    • Name — human label, shows up in the list (e.g. "Slack ops channel", "Custom CRM").
    • URL — https://your-receiver/dtedge-events. Must be public-internet-reachable from the dtedge install (SSRF guard refuses private IPs, cluster-internal hostnames, metadata endpoints).
    • Bearer token (optional) — added as Authorization: Bearer <token> on every delivery. Stored AES-encrypted at rest. Recommended.
    • Active — toggle off to pause deliveries without losing the row.
  4. Save.

Test a webhook​

After save, the row's actions menu has Test. This dispatches a sample payload:

{
"event": "test",
"operation_id": 0,
"type": "test",
"status": "test",
"org_id": "<your-org-id>",
"timestamp": "<now>"
}

Use it to confirm:

  • Your URL is reachable from dtedge
  • The Bearer token (if any) is being sent correctly
  • Your receiver returns 2xx (anything else counts as failure)

Response status appears in a toast.

View delivery history​

Each webhook row has a History drawer. Each entry:

FieldNotes
TimestampWhen the attempt happened
Eventoperation.completed / operation.failed / test
Status codeHTTP response from your URL
Statussuccess / failed
AttemptsHow many tries before this row landed in its final state (1 = first try succeeded)

Retry a failed delivery​

For rows where status: failed and retries exhausted, the actions menu has Retry. Re-queues the payload as a fresh delivery task. Useful when:

  • Your receiver was down during the original window
  • You fixed a bug in the receiver and want to replay missed events

Retry semantics​

A single webhook delivery has 5 retry attempts with exponential backoff:

Attempt 1: immediate
Attempt 2: ~1 min later
Attempt 3: ~2 min
Attempt 4: ~4 min
Attempt 5: ~16 min

If all five fail (2xx never returned), the delivery is marked failed and stays in the history. The receiver is treated as "down for this event" — subsequent events for the same webhook still attempt independently, so missing one doesn't break the stream.

A receiver returning 2xx counts as success even if it discards the payload. We don't try to parse the response body.

URL restrictions​

The save handler refuses:

  • Cluster-internal hostnames (*.svc.cluster.local, .internal)
  • Private IP literals (10.0.0.0/8, 192.168.0.0/16, etc.)
  • Loopback (127.0.0.0/8)
  • Metadata IPs (169.254.169.254, etc.)
  • Non-HTTPS URLs in production (HTTP allowed only in dev mode)

DNS rebinding is defeated by re-checking the resolved IP at dial time, so a public hostname that resolves to a private IP is caught at delivery, not just at save.

These restrictions defeat SSRF — a malicious org admin (or a compromised one) can't use webhooks to probe internal services.

Common errors​

SymptomCause
Save returns WEBHOOK_URL_BLOCKEDURL points at a private IP, cluster-internal hostname, or metadata endpoint
Save returns WEBHOOK_URL_INVALIDNot a valid http(s) URL
Test fails with 401Your receiver requires a different auth mechanism than Bearer
Test fails with timeoutReceiver took > 10 seconds; we don't retry inside a single attempt
Every delivery failsReceiver is down, or rejects our payload shape. Check the History drawer's Status code column.

See also​