Webhooks
Showdini can POST to an endpoint you control when something happens on a demo. Configure webhooks per demo show in the Showdini app.
Events
| Event | Fires when |
|---|---|
lead.report_generated | A visitor finished a demo and their lead report has been generated. |
You can also send a test event from the app to check your endpoint before going live.
Request
Showdini sends a POST with a JSON body.
| Header | Value |
|---|---|
Content-Type | application/json |
X-Webhook-Event | The event name, e.g. lead.report_generated |
Authorization | Bearer <your secret> — only if you configured a secret |
Your endpoint must be a public http or https URL. Private, loopback, and
link-local addresses are rejected, and redirects are not followed.
Delivery
- Timeout: 10 seconds per attempt.
- Retries: up to 2 attempts total, 5 seconds apart.
- Success: any
2xxresponse. Anything else counts as a failure and is retried.
Respond 200 as soon as you have accepted the payload and do the real work
asynchronously — slow endpoints get retried and then given up on.
Every delivery, successful or not, is recorded in the delivery log in the app so you can see the status code and response body we received.
Payload
Every event uses the same envelope:
{
"event": "lead.report_generated",
"demoShowId": "6712f0a1b2c3d4e5f6a7b8c9",
"timestamp": "2026-08-31T10:04:12.512Z",
"data": { }
}
lead.report_generated
data contains the lead and the session it came from:
| Field | Type | Description |
|---|---|---|
sessionId | string | The demo session. |
reportId | string | The generated report. |
demoShowId | string | The demo show the session belongs to. |
demoShowName | string | Human-readable demo name. |
email | string | null | Lead email, if the visitor gave one. |
customFields | object | Values collected by your demo's form fields. |
metadata | object | Your own key/value data. See Metadata. |
leadScore | number | Lead score for the session. |
insights | string | null | AI-generated summary of the conversation. |
timeline | array | Ordered events that happened during the session. |
interactedCta | boolean | Whether the visitor interacted with the CTA. |
interactedCtaAt | string | null | ISO timestamp of that interaction. |
startedAt / endedAt | string | ISO timestamps bounding the session. |
playedClips | array | Which demo clips were played. |
language | string | null | Language the demo ran in. |
detectedLanguages | string[] | Languages detected during the conversation. |
country / city / timezone | string | null | Approximate visitor location. |
userAgent | string | null | Visitor's browser user agent. |
embedded | boolean | Whether the demo ran embedded in another site. |
leadUrl | string | Deep link to the lead in the Showdini app. |
transcript | array | Full conversation transcript. |
Verifying a request
If you set a secret on the webhook, Showdini sends it as a bearer token. Compare it against your stored value before trusting the payload:
app.post("/showdini-webhook", express.json(), (req, res) => {
const token = req.get("authorization")?.replace("Bearer ", "");
if (token !== process.env.SHOWDINI_WEBHOOK_SECRET) {
return res.sendStatus(401);
}
res.sendStatus(200); // acknowledge first
handleLead(req.body).catch(console.error); // then do the work
});
The secret is a shared bearer token, not an HMAC signature of the body. Always serve your webhook endpoint over HTTPS so the token is not sent in the clear.