meta data for this page
API Cookbook — Webhook Patterns
Note on signature headers: LEAST uses two webhook systems with different headers. Collection API webhooks useX-LEAST-Signature. Workflow Webhooks and LMS Rule Engine webhooks useX-WF-Signature. The patterns below apply to both — substitute the correct header name.
Receiving a Webhook (PHP)
<?php // webhook-receiver.php — Collection API webhook $raw = file_get_contents("php://input"); $signature = $_SERVER["HTTP_X_LEAST_SIGNATURE"] ?? ""; // Collection API // For Workflow / LMS Rule webhooks: $_SERVER["HTTP_X_WF_SIGNATURE"] $secret = "your-webhook-secret"; $expected = "sha256=" . hash_hmac("sha256", $raw, $secret); if (!hash_equals($expected, $signature)) { http_response_code(401); exit("Invalid signature"); } http_response_code(200); echo "ok"; $payload = json_decode($raw, true); file_put_contents("/tmp/webhook_queue.jsonl", json_encode($payload) . "\n", FILE_APPEND);
Minimal Node.js Receiver
const express = require("express"); const crypto = require("crypto"); const app = express(); app.use(express.raw({ type: "application/json" })); app.post("/webhook", (req, res) => { const secret = "your-webhook-secret"; // Collection API: "x-least-signature" | Workflow/LMS: "x-wf-signature" const signature = req.headers["x-least-signature"]; const expected = "sha256=" + crypto.createHmac("sha256", secret) .update(req.body).digest("hex"); if (signature !== expected) return res.status(401).send("Bad signature"); const payload = JSON.parse(req.body); console.log("Event:", payload.event); res.status(200).send("ok"); }); app.listen(3000);
Routing Collection API Events
switch ($payload["event"]) { case "record.created": notify_slack("New item: " . $payload["data"]["strD5_1_1"]); break; case "record.action": if ($payload["action_id"] === 8) { // Action 8 = Complete close_external_ticket($payload["record_id"]); } break; case "record.archived": archive_in_crm($payload["record_id"]); break; }
Routing Workflow / LMS Rule Events
For Workflow Webhooks (header: X-WF-Signature):
switch ($payload["event"]) { case "lesson.completed": update_sis_progress($payload["intLessonId"]); break; case "badge.earned": post_to_linkedin($payload["intBadgeId"]); break; case "workflow.completed": notify_hr_system($payload["intInstanceId"]); break; }
Zapier / Make (Integromat)
Both Zapier and Make support a “Webhooks” trigger:
- Create a Zap / Scenario → add a Webhook trigger → copy the generated URL
- In LEAST: collection admin → Webhooks → add the URL; or Admin → Workflow Webhooks for learner events
- Trigger a test event — Zapier / Make catches it — build downstream steps
Signature verification is not supported natively in Zapier's free tier. Use a Code step to verify manually.
See Also
- Webhooks — full reference for both systems
- LMS Automation Rules — event-driven rule engine