Receive real-time notifications when analyses complete, tasks update, and other events occur.
Webhooks let you receive HTTP POST notifications when events occur in your Shelfforce account. Instead of polling for analysis results, register a webhook URL and Shelfforce will notify you as soon as results are ready.
Copy-paste these handlers to get a working webhook receiver. They verify the signature and handle each event type.
import express from "express";import crypto from "crypto";const app = express();app.use(express.json({ verify: (req, _res, buf) => { (req as any).rawBody = buf; } }));const WEBHOOK_SECRET = process.env.SHELFFORCE_WEBHOOK_SECRET!;function verifySignature(rawBody: Buffer, signatureHeader: string): boolean { const parts = Object.fromEntries( signatureHeader.split(",").map((part) => { const [key, value] = part.split("="); return [key, value]; }) ); const timestamp = parts["t"]; const signature = parts["v1"]; if (!timestamp || !signature) return false; // Reject requests older than 5 minutes const now = Math.floor(Date.now() / 1000); if (Math.abs(now - parseInt(timestamp, 10)) > 300) return false; const signedPayload = `${timestamp}.${rawBody.toString()}`; const expected = crypto .createHmac("sha256", WEBHOOK_SECRET) .update(signedPayload) .digest("hex"); return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));}app.post("/webhooks/shelfforce", (req, res) => { const signatureHeader = req.headers["x-shelfforce-signature"] as string; if (!verifySignature((req as any).rawBody, signatureHeader)) { return res.status(401).json({ error: "Invalid signature" }); } // Respond immediately — process asynchronously res.status(200).json({ received: true }); const { event, data } = req.body; switch (event) { case "analysis.completed": console.log(`Analysis ${data.generationId} completed with ${data.productCount} products`); // Fetch full results: GET /api/v1/analyses/{data.generationId} break; case "analysis.failed": console.error(`Analysis ${data.generationId} failed: ${data.error}`); break; case "task.created": console.log(`Task ${data.taskId} created: ${data.title}`); break; case "task.updated": console.log(`Task ${data.taskId} updated: ${data.updatedFields.join(", ")}`); break; case "task.completed": console.log(`Task ${data.taskId} completed — compliance: ${data.complianceScore}%`); break; }});app.listen(3000, () => console.log("Webhook receiver running on port 3000"));
Your webhook endpoint should return a 200 response as quickly as possible. Perform heavy processing (fetching full analysis results, updating your database, etc.) asynchronously after acknowledging receipt.
Every webhook request includes an X-Shelfforce-Signature header that you should verify to ensure the request is authentic and has not been tampered with.The header format is: