Stable event names
demo.completedA visitor reached the end of a meaningful demo session.
visitor.qualifiedThe conversation or behavior shows buying intent and should notify revenue workflows.
handoff.createdA handoff was created or tested for webhook, CRM, email, or automation workflows.
Request contract
Configure an HTTPS endpoint in workspace handoff settings. Demoloop posts JSON with an eight-second timeout. Return any 2xx response to mark delivery successful. Failed deliveries remain saved in Conversations with channel status.
x-demoloop-eventThe event type, for example visitor.qualified.
x-demoloop-deliveryThe unique delivery id. It matches payload.id.
x-demoloop-schema-versionThe payload schema version. Current version: 2026-07-10.
x-demoloop-signatureHMAC-SHA256 signature in the format sha256=<hex>.
Verify the signature
Compute an HMAC-SHA256 digest over the raw request body using the workspace webhook secret. Compare it with the value after sha256= in x-demoloop-signature. Use a constant-time comparison in production.
import crypto from "node:crypto";
function verifyDemoloopWebhook(rawBody, signatureHeader, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
const actual = signatureHeader.replace(/^sha256=/, "");
return crypto.timingSafeEqual(
Buffer.from(actual, "hex"),
Buffer.from(expected, "hex")
);
}Example payload
{
"type": "visitor.qualified",
"version": "2026-07-10",
"id": "evt_018f5b7d4b594b9a93c2e08b1e6d52c2",
"createdAt": "2026-07-10T12:00:00.000Z",
"workspace": {
"id": "wrk_123",
"name": "Acme workspace",
"productName": "Acme",
"websiteUrl": "https://acme.example"
},
"visitor": {
"email": "buyer@example.com",
"language": "English",
"qualification": "qualified",
"profile": {
"company": "Acme",
"role": "VP Sales",
"useCase": "Website product demos"
}
},
"demo": {
"id": "ses_123",
"status": "live",
"productArea": "analytics",
"startedAt": "2026-07-10T11:55:00.000Z",
"endedAt": null,
"summary": null,
"rating": null,
"feedback": null,
"signals": [
"Asked about rollout timeline"
],
"transcript": [
{
"speaker": "visitor",
"text": "Can this qualify demo visitors?",
"at": "2026-07-10T11:57:00.000Z"
},
{
"speaker": "agent",
"text": "Yes. Demoloop records qualification signals and handoff context.",
"at": "2026-07-10T11:57:03.000Z"
}
]
},
"handoff": {
"source": "demoloop",
"test": false,
"reason": "visitor_qualified"
}
}PII redaction
If workspace PII redaction is enabled, transcript email addresses are replaced before outbound delivery.
Secret fallback
Webhook signatures use the workspace webhook secret. If one is not configured, Demoloop falls back to the workspace widget key.
Retry expectation
Today, non-2xx responses are recorded as failed handoff channels. Treat webhook handlers as idempotent using payload.id.
Before going live
Use HTTPS and respond within eight seconds.
Store processed payload ids to avoid duplicate side effects.
Verify x-demoloop-signature before trusting payload data.
Test with the Growth handoff test button before routing production leads.