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.
Copy
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:
If your webhook endpoint returns a non-2xx status code or is unreachable, Shelfforce retries the delivery up to 3 times with exponential backoff:
Attempt
Delay
1st retry
500ms
2nd retry
1 second
3rd retry
2 seconds
After all retries are exhausted, the webhook delivery is marked as failed. You can view failed deliveries in the Webhooks settings page in the dashboard.