Webhooks
Quickstart
Get your first SmartComply webhook delivering events in four steps. This guide takes about five minutes if you already have an HTTPS endpoint ready.
Prerequisites
- A SmartComply API key (
sc_live_...) - An HTTPS endpoint that can receive POST requests
No endpoint yet? Use a tunnel tool to expose a local server, or deploy a minimal receiver first and come back.
Register an endpoint
Call the Create Endpoint API (or use the dashboard at Settings → Webhooks → Add endpoint). Specify your URL and the events you want to receive.
curl -X POST https://app.smartcomply.app/api/v1/webhooks/endpoints \
-H "Authorization: Bearer sc_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.example/webhooks/smartcomply",
"events": ["test.submitted", "notice.created"],
"description": "Production receiver"
}'The response includes your signing_secret. This is the only time it is returned in full — copy it now.
{
"id": "whe_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"url": "https://your-app.example/webhooks/smartcomply",
"events": ["test.submitted", "notice.created"],
"description": "Production receiver",
"status": "active",
"signing_secret": "whsec_k7Lm9PqRsTuVwXyZ...",
"created_at": "2026-05-05T14:00:00.000Z"
}Save the signing secret
Store signing_secretin your application's environment variables — for example as SMARTCOMPLY_WEBHOOK_SECRET. You will use it to verify that incoming deliveries actually came from SmartComply and have not been tampered with.
POST .../rotate-secret endpoint.Verify with a test event
Send a test event to confirm your endpoint is reachable and your signature verification works. You can do this from the dashboard (click Send test event on the endpoint detail page) or via the API:
curl -X POST https://app.smartcomply.app/api/v1/webhooks/endpoints/whe_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6/test \
-H "Authorization: Bearer sc_live_YOUR_API_KEY"SmartComply fires a webhook.testevent with a synthetic payload. Check your server logs — you should see the delivery arrive within a few seconds. The delivery will appear in your endpoint's delivery log in the dashboard so you can inspect the full request/response cycle.
Handle real events
Here is a complete Express.js receiver that verifies the signature and switches on event type. Adapt to your framework of choice — the important parts are: read the raw body, verify the HMAC, respond with 200 fast, then process asynchronously.
import express from "express";
import crypto from "node:crypto";
const app = express();
// IMPORTANT: use the raw body for signature verification
app.post(
"/webhooks/smartcomply",
express.raw({ type: "application/json" }),
(req, res) => {
const secret = process.env.SMARTCOMPLY_WEBHOOK_SECRET; // whsec_...
const sigHeader = req.headers["x-smartcomply-signature"];
// --- 1. Verify the signature ---
const parts = Object.fromEntries(
sigHeader.split(",").map((p) => p.split("="))
);
const t = Number(parts.t);
const v1 = parts.v1;
const body = req.body.toString();
const age = Math.floor(Date.now() / 1000) - t;
if (age > 300) return res.status(400).send("Signature too old");
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${body}`)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))) {
return res.status(400).send("Invalid signature");
}
// --- 2. Parse the event ---
const event = JSON.parse(body);
console.log(`Received ${event.type} (id: ${event.id})`);
// --- 3. Handle by type ---
switch (event.type) {
case "test.submitted":
// sync the test result to your system
break;
case "notice.created":
// open a case in your ticketing system
break;
case "webhook.test":
// test event — just log it
console.log("Test event received successfully");
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}
// --- 4. Ack fast! ---
res.status(200).json({ received: true });
}
);
app.listen(3000, () => console.log("Webhook receiver running on :3000"));Key points from the code above:
- Raw body parsing.
express.raw()gives you the un-parsed request body as a Buffer — this is what you sign against. If your framework parses JSON before you get the raw bytes, the signature will not match. - Clock-skew tolerance. The 300-second (5-minute) window protects against replay attacks while accommodating reasonable clock drift.
- Fast acknowledgment. Return
200before doing any heavy processing. SmartComply times out after 10 seconds. - Idempotency. Use
event.idto deduplicate. Store it in your database and skip events you have already processed.
What's next
- Webhook integration guide — deep dive into signature math, retry policy, dead-letter behavior, and best practices.
- Event catalog — full list of event types with example payloads.
- API reference — interactive OpenAPI docs for all SmartComply endpoints.