Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.cyberdesk.io/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Webhooks notify your app when something happens in Cyberdesk. Instead of polling, subscribe to the event types you care about (see the Event Catalog in the Webhooks portal). The most common one is run_complete, which we’ll use below as an example.
Typical flow: create a run with the SDK, return immediately to your user, and update your system when you receive the webhook.

1) Enable Webhooks in the Dashboard

  1. Go to the Cyberdesk Dashboard → Webhooks.
  2. Click “Activate Webhooks” (creates a Svix Application for your organization).
  3. Click “Add Endpoint” and enter your HTTPS URL.
  4. Subscribe to the event types you need (for getting started, run_complete is typical).
  5. Copy the endpoint’s signing secret (whsec_...).
Each endpoint has its own secret. Keep separate endpoints/secrets for dev and prod.

2) Implement the Endpoint (verify signatures)

Install dependencies

npm install cyberdesk svix
# or
yarn add cyberdesk svix
# or
pnpm add cyberdesk svix
We’re working on a managed Webhooks SDK with built‑in verification and typing. If you’d like this prioritized, please let the team know.

Endpoint handlers

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

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

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);
    // Narrowed type: payload is RunCompletedEvent; run is fully typed
    const run: RunResponse = payload.run;
    // idempotency: upsert on headers["svix-id"] or payload.event_id
    // handle success/error/cancelled
    res.status(200).end();
  } catch (err) {
    if (err instanceof WebhookVerificationError) return res.status(400).end();
    res.status(500).end();
  }
});
import { createCyberdeskClient } from "cyberdesk";
import type { RunResponse } from "cyberdesk";

async function onRunComplete(run: RunResponse) {
  const client = createCyberdeskClient(process.env.CYBERDESK_API_KEY!);

  // Example A: Start a follow-up workflow
  await client.runs.create({
    workflow_id: process.env.NEXT_WORKFLOW_ID!,
    input_values: { summary: run.output_data?.summary }
  });

  // Example B: Persist and enqueue for async processing
  await db.runs.upsert({ id: run.id, status: run.status, output: run.output_data });
  await queue.enqueue("postprocess-run", { runId: run.id });
}
Type safety:
  • TypeScript: use a type guard (like assertRunCompletedEvent) to narrow the Svix‑verified payload to RunCompletedEvent, then type run as RunResponse.
  • Python: RunCompletedEvent.from_dict(data) returns a typed attrs object; some IDEs may still show Any | RunResponse for evt.run, so cast(RunResponse, evt.run) helps IDEs while remaining safe after validation. Note: The SDK uses attrs classes, not Pydantic, so use from_dict() not model_validate().

3) Event payload

Payloads vary by event type. Refer to the Event Catalog in the portal for up‑to‑date schemas. Example for run_complete:
{
  "event_id": "uuid",
  "event_type": "run_complete",
  "occurred_at": "2025-08-16T19:19:44Z",
  "run": { /* RunResponse: id, workflow_id, status, error, output_data, input_values, attachment ids, created_at, ... */ }
}
Use run.status to branch your logic and output_data or attachments to continue your process.
Sensitive inputs: plaintext secrets are never included in webhook payloads. If a run used sensitive variables, you’ll see run.sensitive_input_aliases, which maps each sensitive input key (for example, password) to the secure secret identifier used during execution. Actual values stay in the secure vault and are deleted after completion.

4) Test

  • In the Webhooks tab, send a test event to your endpoint.
  • Use the Logs/Activity views to inspect payloads and delivery attempts, replay failures, and recover from downtime.

Learn more

Detailed Webhook Guide

Signature details, retries, troubleshooting, and recovery patterns.

Webhook Transformations

Reshape or reduce webhook payloads before they reach your endpoint.

React to Post-run Checks

Handle Post-run Check results from run_complete events.

Workflow Chains

Run multi-step workflows on one reserved session.