Connect DemoMate to Zapier

Route lead, view, and CTA events from your demos into 6,000+ Zapier apps using a Catch Hook and DemoMate's signed webhooks — with the exact payload each event sends. Requires a paid Zapier plan (Catch Hook is a premium Zapier app).

DemoMate sends every demo event as a signed webhook, and Zapier can receive one directly. That means anything Zapier connects to — your CRM, a Slack channel, a Google Sheet, an email sequence — can be driven by what happens inside a demo, with no code.

There is nothing to install. A Zapier "Catch Hook" gives you a URL; you paste that URL into DemoMate as a webhook endpoint, choose which events it should receive, and your Zap runs on every matching event.

Zapier plan requirement. Catch Hook belongs to Webhooks by Zapier, which is a premium Zapier app — a paid Zapier plan is required to use it. This is a Zapier restriction, not a DemoMate one: webhooks are available on every DemoMate plan.

Set it up

  1. In Zapier, create a new Zap and choose Webhooks by Zapier as the trigger app.
  2. Pick the Catch Hook event, then continue. Zapier shows you a custom webhook URL — copy it. Leave "Pick off a child key" empty; DemoMate posts the whole event as the body.
  3. In DemoMate, go to Settings → Webhooks and click Add endpoint.
  4. Paste the Zapier URL, tick the events you want the Zap to receive, and choose a delivery scope (below).
  5. Create the endpoint. Copy the whsec_… signing secret shown once — you will need it if you verify signatures.
  6. Open the endpoint's menu and choose Send test event, then click Test trigger in Zapier. This confirms the plumbing end to end — but note what it sends: a webhook.test event carrying only v, event, occurredAt, and a message. It deliberately carries no viewer data, so Zapier will not learn the real field names from it.
  7. Add your action step (create a CRM contact, post to Slack, append a row) and map the DemoMate fields onto it. To get the real field names into Zapier's mapper, either trigger one real event (open the demo, or submit its lead form) and re-run Test trigger, or enter the field names by hand from the schemas below. Then publish the Zap.

One Zap = one Catch Hook URL = one DemoMate endpoint. To drive several different Zaps, create a separate endpoint per Zap; you can have up to five endpoints per organization.

Choosing events and scope

Four events are subscribable. Tick only the ones your Zap acts on — an endpoint that receives events it ignores just burns Zapier tasks.

EventFires when
lead.capturedA viewer submits a lead form inside the demo
demo.viewedA viewer opens the demo and starts a session
demo.completedA viewer reaches the end of the demo
cta.clickedA viewer clicks a call-to-action inside the demo

Delivery scope is set on the endpoint:

  • Identified viewers only (default) — deliver only when we know who the viewer is. Best for CRM and sales-alert Zaps, which need an email address to act on.
  • All sessions — deliver for anonymous sessions too. Use for volume dashboards and counters. Anonymous deliveries carry viewer: null.

What each event sends

Every payload is JSON, carries v (the payload version, currently 1), the event name, and occurredAt as an ISO-8601 UTC timestamp. Fields that have no honest value are null rather than omitted or invented — Zapier will show them as empty.

lead.captured

The strongest signal: a viewer filled in a lead form inside the demo. answers holds any extra form questions you asked, in the order they were answered.

lead.source is always form today — this event fires only on lead-form submission. (gate and url are reserved in the schema for viewers identified by an email gate or an identifying link; those identities are recorded against the session and reach you on demo.viewed / demo.completed / cta.clicked as viewer, but they do not emit lead.captured.) Don't build a Zap filtered on lead.source exactly matching gate or url — it can never fire.

{
  "v": 1,
  "event": "lead.captured",
  "occurredAt": "2026-08-04T10:00:00.000Z",
  "demo": { "id": "demo_1", "title": "Onboarding tour", "version": 3 },
  "lead": {
    "id": "lead_1",
    "email": "[email protected]",
    "name": "Dana Reed",
    "source": "form"
  },
  "answers": [{ "label": "Company size", "value": "51-200" }],
  "intent": { "score": 72, "band": "high" }
}

demo.viewed

link identifies which share link was opened (null for a demo opened outside a named link). session.identified tells you whether this session has been tied to a known viewer; when it has, viewer carries the email and name.

{
  "v": 1,
  "event": "demo.viewed",
  "occurredAt": "2026-08-04T10:00:00.000Z",
  "demo": { "id": "demo_1", "title": "Onboarding tour", "version": 3 },
  "link": { "id": "link_1", "name": "Outbound — Q3" },
  "session": { "id": "sess_1", "identified": true },
  "viewer": { "email": "[email protected]", "name": "Dana Reed" },
  "intent": { "score": 72, "band": "high" }
}

demo.completed

Identical in shape to demo.viewed — the same session, reported when the viewer reaches the end.

{
  "v": 1,
  "event": "demo.completed",
  "occurredAt": "2026-08-04T10:00:00.000Z",
  "demo": { "id": "demo_1", "title": "Onboarding tour", "version": 3 },
  "link": { "id": "link_1", "name": "Outbound — Q3" },
  "session": { "id": "sess_1", "identified": true },
  "viewer": { "email": "[email protected]", "name": "Dana Reed" },
  "intent": { "score": 72, "band": "high" }
}

cta.clicked

The view payload plus which step the CTA lived on and the url it pointed at.

{
  "v": 1,
  "event": "cta.clicked",
  "occurredAt": "2026-08-04T10:00:00.000Z",
  "demo": { "id": "demo_1", "title": "Onboarding tour", "version": 3 },
  "link": { "id": "link_1", "name": "Outbound — Q3" },
  "session": { "id": "sess_1", "identified": true },
  "viewer": { "email": "[email protected]", "name": "Dana Reed" },
  "step": 4,
  "url": "https://acme.com/pricing"
}

About intent

intent is an optional engagement signal computed from the session's own event stream — score 0–100 and a band of low, medium, or high. It is null when there is no honest signal (for example a bounce). We never fabricate a score to fill the field, so a Zap that filters on intent.band should also handle it being empty.

Filtering inside Zapier

Because one endpoint can carry several event types, a Zap that should only act on one of them needs a guard. Add a Filter by Zapier step immediately after the trigger and continue only when event exactly matches the event you want — for example lead.captured.

The same trick narrows a Zap to a single demo (demo.id), a single share link (link.id), or high-intent viewers only (intent.band exactly matches high).

Verifying signatures

Every delivery is HMAC-signed, so you can prove it came from DemoMate. Zapier's Catch Hook does not verify the signature for you — it accepts any POST to that URL. Treat the Catch Hook URL as a secret, and if a Zap acts on something sensitive, verify the signature in a Code by Zapier step (or in your own service) using the headers described in Verifying webhook signatures.

Retries, failures, and the delivery log

Delivery behaviour is the same as any DemoMate webhook: a non-2xx response is retried with exponential backoff up to five attempts, and an endpoint that fails 20 times in a row is automatically disabled. Settings → Webhooks → View deliveries shows the last 50 deliveries for an endpoint — status, response code, and the body sent — and lets you re-send one. If a Zap misses an event, check that log first.

Settled deliveries are kept for 30 days and then pruned, so the log is a recent-history view, not an archive. A delivery whose lead was later deleted shows "Contents erased" instead of its body and cannot be re-sent: deleting a lead erases their data from the delivery queue too, and re-sending it is exactly what erasure forbids. Zapier keeps its own task history independently of ours.

Endpoint URLs must be https:// — Zapier's Catch Hook URLs already are.