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
JSON (application/json)
HMAC-SHA256 (hex encoded)
10,000ms (10 seconds)
Disabled after 10 consecutive failures
Every HTTP POST request sent to your webhook URL includes the following custom headers:
| Header | Description | Example |
|---|---|---|
| x-zynocode-signature | Timestamp and HMAC-SHA256 signature | t=1729810000000,v1=a1b2c3d4... |
| x-zynocode-event | Canonical event name | tool.execution_completed |
| x-zynocode-delivery | Unique delivery / event UUID | evt_8f3a9e21-4d1b... |
| User-Agent | HTTP client identifier | Zynocode-Webhooks/1.0 |
Supported Event Types
Configure an endpoint to listen for specific events, or pass * to receive all events.
| Event Identifier | Trigger Condition |
|---|---|
| tool.execution_completed | A server-side tool execution completed successfully. |
| tool.execution_failed | A server-side tool execution failed with an error. |
| billing.payment_captured | Razorpay payment succeeded and settled into workspace credits. |
| billing.payment_failed | Payment attempt was declined or timed out. |
| billing.subscription_cancelled | A workspace plan or recurring billing subscription ended. |
| billing.credits_purchased | Credit balance topped up manually or via enterprise invoice. |
| workspace.member_added | New collaborator accepted invite to the workspace. |
| workspace.member_removed | Member was removed from the workspace. |
| workspace.role_changed | Member permissions upgraded or downgraded (admin/member). |
| security.api_key_created | A new API access key was generated. |
| security.api_key_revoked | An 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:
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:
Register a new endpoint. Body: {"url": "https://api.example.com/webhooks", "events": ["*"], "workspaceId": "ws_..."}. Returns the rawSecret once.
List registered webhook endpoints, subscribed event arrays, failure counts, and last delivery timestamps.
Send an instant test ping payload (eventName: "ping") to verify that your receiver endpoint parses signatures and returns HTTP 200.