Introduction

Webhooks are HTTPS POST requests sent from Cyberdesk to your service when events happen (e.g., a workflow run completes). They eliminate polling and let you react in real-time. The high-level steps:
  1. Activate Webhooks in the Dashboard (creates a Svix Application for your org).
  2. Add an endpoint URL and subscribe to run_complete.
  3. Implement your endpoint to verify signatures and process events.
  4. Test and monitor via the embedded Webhooks portal.

Events and Event Types

Cyberdesk webhooks are organized by event types (e.g., run_complete). You can browse the full, always-up-to-date list and their schemas in the Event Catalog inside the Webhooks portal. As of today, run_complete is the most commonly used type and fires when a workflow run reaches a terminal state (success, error, or cancelled). Its payload includes the full Run object (see API reference for RunResponse).

Adding an Endpoint

  1. Go to Dashboard → Webhooks.
  2. Click “Add Endpoint” and enter your publicly reachable HTTPS URL.
  3. Select the run_complete event type (or accept all for initial testing).
  4. Copy the generated endpoint signing secret (whsec_...).
Keep separate endpoints/secrets for each environment (dev, staging, prod).

Testing Endpoints

Use the “Testing” tools in the Webhooks portal to send example events to your endpoint. Inspect payloads, responses, and message attempts under Logs/Activity. You can replay messages individually or recover all failed messages since a timestamp.
We’re building a managed Webhooks SDK with built‑in verification and typing. If you want this prioritized, please let the team know.

Verifying Signatures

Always verify signatures to ensure messages originate from Cyberdesk. We use Svix headers and signing format. Headers sent with every webhook:
  • svix-id – unique message id (use for idempotency)
  • svix-timestamp – Unix timestamp
  • svix-signature – signature over the raw request body

Install dependencies

npm install cyberdesk svix
# or
yarn add cyberdesk svix
# or
pnpm add cyberdesk svix

Endpoint handlers

import express from "express";
import { Webhook, WebhookVerificationError } from "svix";
import { createCyberdeskClient } from "cyberdesk";
import type { RunCompletedEvent } from "cyberdesk";

const app = express();
app.use(express.raw({ type: "application/json" }));

function assertRunCompletedEvent(x: unknown): asserts x is RunCompletedEvent {
  if (!x || (x as any).event_type !== "run_complete" || !(x as any).run) {
    throw new Error("Invalid run_complete payload");
  }
}

app.post("/webhooks/cyberdesk", (req, res) => {
  const secret = process.env.SVIX_WEBHOOK_SECRET!;
  const wh = new Webhook(secret);
  const headers = {
    "svix-id": req.header("svix-id")!,
    "svix-timestamp": req.header("svix-timestamp")!,
    "svix-signature": req.header("svix-signature")!,
  };
  try {
    const payload = wh.verify(req.body, headers);
    assertRunCompletedEvent(payload);
    // process payload
    res.status(200).end();
  } catch (err) {
    if (err instanceof WebhookVerificationError) return res.status(400).end();
    res.status(500).end();
  }
});
Verify against the exact raw request body. Do not re-stringify JSON before verification.

Retry Mechanism

Cyberdesk (via Svix) retries failed deliveries using exponential backoff:
  • Immediately
  • 5 seconds
  • 5 minutes
  • 30 minutes
  • 2 hours
  • 5 hours
  • 10 hours
  • 10 hours
A response with HTTP 2xx is considered success. Avoid long processing in the webhook handler—enqueue work and return 200 quickly to prevent timeouts. Manual retries: from the portal you can resend single messages or recover all failed messages since a given time.

Troubleshooting & Failure Recovery

Common pitfalls:
  • Not using raw body for signature verification
  • Using the wrong endpoint secret (each endpoint has its own secret)
  • Returning non-2xx status for successful processing
  • Handler timeouts (do heavy work asynchronously)
Failure recovery:
  • Re-enable disabled endpoints in the portal
  • Replay failed messages individually or recover all since a timestamp

Using run_complete Data

run_complete includes the full RunResponse. Useful patterns:
  • Trigger the next workflow in your pipeline
  • Update your job table with run.status and output_data
  • Persist output_attachment_ids and fetch/download files as needed
  • Correlate input_values to your original request (e.g., patient_id)

Example: TypeScript handler

if (payload.event_type === "run_complete") {
  const run = payload.run;
  await db.runs.upsert({ id: run.id, status: run.status, output: run.output_data });
  // kick off a follow-up Cyberdesk run
  const client = createCyberdeskClient(process.env.CYBERDESK_API_KEY!);
  await client.runs.create({ workflow_id: process.env.NEXT_WORKFLOW_ID! });
  if (run.output_attachment_ids?.length) {
    // display download links using Cyberdesk attachment APIs
  }
}

Example: Python handler

if data.get("event_type") == "run_complete":
    run = data["run"]
    save_status(run["id"], run["status"], run.get("output_data"))
    # kick off a follow-up Cyberdesk run
    client = CyberdeskClient(os.environ["CYBERDESK_API_KEY"])
    await client.runs.create({"workflow_id": os.environ["NEXT_WORKFLOW_ID"]})
    # fetch/download attachments later if needed

Idempotency

Deduplicate using svix-id (header) or event_id (payload). Store processed ids and ignore duplicates.

Security Checklist

  • Verify signatures and timestamp
  • Use HTTPS only
  • Rotate secrets when needed
  • Return 2xx promptly; process work asynchronously

See Also

  • Webhooks Quickstart
  • API Reference → Event types and Run schema