Developer Portal
Event Subscriptions

Webhooks Guide & Reference

Receive cryptographically signed HTTP POST notifications whenever asynchronous events occur in your workspace, such as payment settlement, membership updates, or tool execution outcomes.

Server-Side Execution Notice

Webhooks are triggered and dispatched from Zynocode Labs cloud infrastructure. In-browser client-side computations do not generate outbound webhooks. Only authenticated server operations (API executions, Razorpay billing events, workspace membership alterations, and API key rotations) emit outbound webhook dispatches.

Webhook Architecture & Delivery Headers

Payload Format

JSON (application/json)

Signature Algorithm

HMAC-SHA256 (hex encoded)

HTTP Timeout

10,000ms (10 seconds)

Circuit Breaker

Disabled after 10 consecutive failures

Every HTTP POST request sent to your webhook URL includes the following custom headers:

HeaderDescriptionExample
x-zynocode-signatureTimestamp and HMAC-SHA256 signaturet=1729810000000,v1=a1b2c3d4...
x-zynocode-eventCanonical event nametool.execution_completed
x-zynocode-deliveryUnique delivery / event UUIDevt_8f3a9e21-4d1b...
User-AgentHTTP client identifierZynocode-Webhooks/1.0

Supported Event Types

Configure an endpoint to listen for specific events, or pass * to receive all events.

Event IdentifierTrigger Condition
tool.execution_completedA server-side tool execution completed successfully.
tool.execution_failedA server-side tool execution failed with an error.
billing.payment_capturedRazorpay payment succeeded and settled into workspace credits.
billing.payment_failedPayment attempt was declined or timed out.
billing.subscription_cancelledA workspace plan or recurring billing subscription ended.
billing.credits_purchasedCredit balance topped up manually or via enterprise invoice.
workspace.member_addedNew collaborator accepted invite to the workspace.
workspace.member_removedMember was removed from the workspace.
workspace.role_changedMember permissions upgraded or downgraded (admin/member).
security.api_key_createdA new API access key was generated.
security.api_key_revokedAn API key was revoked or expired.

Verifying Webhook Signatures

Zynocode signs all outbound webhooks with HMAC-SHA256 using your endpoint secret (whsec_...). The signed payload string is constructed as:

signatureData = "${eventId}.${timestamp}.${rawBody}"

Use the following timing-safe verification snippet in your Node.js or TypeScript server:

verify-webhook.tsStandard Node.js crypto
import crypto from "crypto";

export interface VerifyWebhookOptions {
  rawBody: string;             // Exact raw request text body
  signatureHeader: string;     // Value of 'x-zynocode-signature' header
  secret: string;              // Your 'whsec_...' endpoint secret
  toleranceSeconds?: number;   // Max age in seconds (default: 300)
}

export function verifyZynocodeWebhook({
  rawBody,
  signatureHeader,
  secret,
  toleranceSeconds = 300,
}: VerifyWebhookOptions): boolean {
  if (!signatureHeader || !rawBody || !secret) return false;

  // 1. Parse header components (e.g. t=1729810000000,v1=abcdef...)
  const parts = signatureHeader.split(",").reduce<Record<string, string>>((acc, pair) => {
    const [k, v] = pair.split("=");
    if (k && v) acc[k.trim()] = v.trim();
    return acc;
  }, {});

  const timestamp = parts["t"];
  const expectedSignature = parts["v1"];
  if (!timestamp || !expectedSignature) return false;

  // 2. Prevent replay attacks (verify timestamp freshness)
  const tsNum = parseInt(timestamp, 10);
  if (isNaN(tsNum) || Math.abs(Date.now() - tsNum) > toleranceSeconds * 1000) {
    return false;
  }

  // 3. Extract eventId from JSON body
  let eventId = "";
  try {
    const parsed = JSON.parse(rawBody);
    eventId = parsed.eventId || "";
  } catch {
    return false;
  }

  // 4. Compute expected HMAC-SHA256 signature
  const signatureData = `${eventId}.${timestamp}.${rawBody}`;
  const computedSignature = crypto
    .createHmac("sha256", secret)
    .update(signatureData)
    .digest("hex");

  // 5. Compare signatures using timing-safe buffer comparison
  if (computedSignature.length !== expectedSignature.length) return false;
  return crypto.timingSafeEqual(
    Buffer.from(computedSignature, "utf8"),
    Buffer.from(expectedSignature, "utf8")
  );
}

Sample Event Payload

{
  "eventId": "evt_9b14f820-21a4-4cc5-a3d2-3c81e9f1a0e1",
  "eventName": "tool.execution_completed",
  "userId": "usr_99881122",
  "workspaceId": "ws_11223344",
  "toolSlug": "word-counter",
  "status": "completed",
  "executionId": "exec_abc123xyz789",
  "result": {
    "words": 42,
    "characters": 278,
    "sentences": 3,
    "paragraphs": 1
  },
  "timestamp": 1729810000000
}

Endpoint Management

Manage endpoints programmatically or test deliveries using the workspace webhooks API:

POST/api/user/webhooks

Register a new endpoint. Body: {"url": "https://api.example.com/webhooks", "events": ["*"], "workspaceId": "ws_..."}. Returns the rawSecret once.

GET/api/user/webhooks?workspaceId=...

List registered webhook endpoints, subscribed event arrays, failure counts, and last delivery timestamps.

POST/api/user/webhooks/:id/test

Send an instant test ping payload (eventName: "ping") to verify that your receiver endpoint parses signatures and returns HTTP 200.

← Model Context Protocol (MCP) GuideREST API Reference