Webhooks allow you to receive real-time notifications when your Parallel Task Runs complete, eliminating the need for constant polling—especially for long-running or research-intensive tasks.
Our webhooks follow standard webhook conventions to ensure security and interoperability.
You can view and manage the secret keys used to sign webhook calls in Settings → Webhooks.
We currently support task_run.status event on task run completions.When a task run finishes, either with a success or a failure, we will send a
POST request to your configured webhook endpoint.
Signatures are space-delimited per the Standard Webhooks format. The webhook-signature
header can include multiple entries separated by spaces; you should check each entry
until you find one which matches the signature generated by your webhook secret.Under normal circumstances there will only be one signature in the webhook-signature
header, but there may be multiple if you rotate your webhook secret without immediately
expiring the old secrets.
Webhook requests are signed using HMAC-SHA256 with standard Base64 (RFC 4648) encoding with padding. The signature header is formatted as v1,<base64 signature> where <base64 signature> is computed over the payload below.
Copy
Ask AI
<webhook-id>.<webhook-timestamp>.<payload>
Where:
<webhook-id>: The value of the webhook-id header
<webhook-timestamp>: The value of the webhook-timestamp header
<payload>: The exact JSON body of the webhook request
You must parse the version and the signature before verifying. The webhook-signature header uses space-delimited signatures; check each signature until one matches.Here’s how you may verify the signature:
Copy
Ask AI
import crypto from "crypto";function computeSignature( secret: string, webhookId: string, webhookTimestamp: string, body: string | Buffer): string { const payload = `${webhookId}.${webhookTimestamp}.${body.toString()}`; const digest = crypto.createHmac("sha256", secret).update(payload).digest(); return digest.toString("base64"); // standard Base64 with padding}function isValidSignature( webhookSignatureHeader: string, expectedSignature: string): boolean { // Header may contain multiple space-delimited entries; each is "v1,<sig>" const signatures = webhookSignatureHeader.split(" "); for (const part of signatures) { const [, sig] = part.split(",", 2); if ( crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expectedSignature)) ) { return true; } } return false;}// Example usage in an Express endpointimport express from "express";const app = express();app.post( "/webhooks/parallel", express.raw({ type: "application/json" }), (req, res) => { const webhookId = req.headers["webhook-id"] as string; const webhookTimestamp = req.headers["webhook-timestamp"] as string; const webhookSignature = req.headers["webhook-signature"] as string; const secret = process.env.PARALLEL_WEBHOOK_SECRET!; const expectedSignature = computeSignature( secret, webhookId, webhookTimestamp, req.body ); if (!isValidSignature(webhookSignature, expectedSignature)) { return res.status(401).send("Invalid signature"); } // Parse and process the webhook payload const payload = JSON.parse(req.body.toString()); console.log("Webhook received:", payload); // Process the task run status update if (payload.type === "task_run.status") { const taskRun = payload.data; console.log(`Task ${taskRun.run_id} status: ${taskRun.status}`); // Your business logic here } res.status(200).send("OK"); });
Always verify HMAC signatures using your account webhook secret from Settings → Webhooks to ensure webhook authenticity. Ensure that you are calculating signatures using the proper process as shown above.
Although not common, duplicate events may be sent to the configured webhook URL.
Ensure your webhook handler can detect and safely ignore duplicate events.