Skip to content

Webhooks

Webhooks let WebinarStack notify another system when something happens in or around a webinar. Instead of exporting data manually, you can send attendee activity to a CRM, sales workflow, data warehouse, no-code automation tool, or custom backend as soon as the event occurs.

This guide explains the webhook options available in WebinarStack, how to configure them, how to build a secure receiver, how to test delivery, and how to ask an AI assistant for help without exposing secrets.

WebinarStack has three webhook-related surfaces. They solve different problems.

Webhook Surface Direction Where You Configure It Best For
Automation webhooks WebinarStack sends events to your system. Webinar editor > Automations. Sending one trigger-based action to your CRM, backend, or no-code tool.
API webhook subscriptions WebinarStack sends events to your system. WebinarStack REST API. Developer-managed subscriptions across multiple event types with one shared secret.
Revenue webhooks Your payment provider sends purchase events into WebinarStack. Settings > Revenue. Attributing Stripe or generic HMAC purchase, refund, and chargeback events back to attendees and offers.

Most users who say “set up a webhook” mean an automation webhook. Start there unless you specifically need an API-managed subscription or revenue attribution.

An outbound webhook starts with a webinar automation rule.

  1. A webinar event happens, such as a registration, join, no-show, offer click, poll response, or chat message.
  2. WebinarStack evaluates enabled automation rules for that webinar and trigger.
  3. If the rule conditions pass, WebinarStack queues the webhook action.
  4. The webhook worker builds a JSON payload.
  5. WebinarStack signs the exact JSON body with the rule’s webhook secret.
  6. WebinarStack sends an HTTP POST request to your destination URL.
  7. Your receiver verifies the signature, processes the event, and returns a 2xx response.
  8. WebinarStack records the delivery status, response code, and a truncated response body.

The delivery is asynchronous. The attendee does not wait for your webhook receiver to finish.

Dashboard automation webhooks are the easiest way to send one event-driven webhook from a webinar.

Use a dashboard automation webhook when:

  • You want a webhook for one webinar.
  • You want to configure the trigger and conditions visually.
  • You want access to all automation triggers, including poll, chat, watch-time, adaptive branch, and time-based triggers.
  • You do not need to manage subscriptions through the API.

Automations tab showing a saved webhook automation rule for watched percentage with the rule enabled

  1. Open the webinar in the dashboard.
  2. Go to the Automations tab.
  3. Click Add automation.
  4. Enter a Rule Name.
  5. Leave the rule Enabled unless you are still preparing the destination.
  6. Choose the trigger under When this happens.
  7. Add conditions if the rule should only fire for a narrower audience.
  8. Set Action Type to Webhook.
  9. Enter the Destination URL.
  10. Click Save Automation.

For a new dashboard rule, the webhook secret is generated after you save. Reopen the rule to copy it.

Field Meaning
Rule Name Internal label for your team. Use a specific name such as Send high-intent attendees to CRM.
Enabled Whether the rule can fire. Disable it while preparing a receiver.
Trigger The event that starts the rule.
Conditions Extra filters that must all pass before the webhook sends. Conditions use AND logic.
Action Type Choose Webhook.
Destination URL The URL WebinarStack posts to. Use HTTPS in production.
Webhook Secret Generated by WebinarStack. Store it in your receiver’s environment variables and use it to verify signatures.

Edit Automation sheet showing a webhook rule with trigger conditions, destination URL, signing secret, and delivery history

Dashboard automation webhooks can use the same triggers as other automation actions.

Trigger When It Fires Useful For
Registered An attendee registers. Create or update a CRM lead.
Joined webinar An attendee joins a session. Mark attendance in a CRM.
Left webinar An attendee leaves. Trigger exit follow-up workflows.
Didn’t show up A registrant misses the session. Send no-show data to a sales or nurture workflow.
Watched at least X minutes Watch time reaches a minute threshold. Identify engaged attendees.
Watched at least X% Watch completion reaches a percentage threshold. Segment high-intent viewers.
Completed the video The attendee reaches completion. Trigger completion certificates, offers, or sales tasks.
Clicked an offer An attendee clicks an offer or CTA. Notify sales, tag a lead, or start purchase follow-up.
Responded to a poll An attendee answers a poll. Route based on answer or enrich a contact profile.
Sent a chat message An attendee sends a chat message. Create support or sales tasks from questions.
Before session A scheduled session is approaching. Send data to an external reminder system.
After session Webinar content ends. Trigger post-webinar sync or reporting workflows.
Time elapsed after event A delay passes after registration or session end. Coordinate delayed workflows outside WebinarStack.
Branch taken An adaptive webinar viewer takes a selected branch. Track personalized path selection.
Segment completed An adaptive webinar viewer completes a selected segment. Trigger segment-specific follow-up.

Before-session triggers require scheduled sessions. If a webinar only uses just-in-time sessions, before-session webhook rules may not fire.

Conditions let you narrow when a webhook sends. All conditions must be true.

Examples:

  • Send only when watch percentage is greater than or equal to 75.
  • Send only when joined is true.
  • Send only when a specific offer was clicked.
  • Send only when a specific poll answer was selected.
  • Send only when a chat message contains a keyword such as pricing.

Use conditions when a trigger is too broad by itself. For example, Clicked an offer sends for every matching offer click unless you select a specific offer or add a condition.

API webhook subscriptions are for developers who want to manage webhook configuration programmatically.

To use these endpoints, create a WebinarStack API key from Settings > API Keys with REST API access and the automations:read / automations:write scopes needed for the operation.

Use API subscriptions when:

  • You want one subscription that listens to multiple event types.
  • You want to create, update, disable, delete, rotate, and redeliver through the REST API.
  • You are building an integration for multiple webinars.
  • You want URL validation that requires a public HTTPS destination.

API subscriptions are separate from standalone dashboard webhook automation rules. The API list endpoints return webhook subscriptions created through the API subscription surface, not every standalone webhook rule created in the dashboard.

API subscriptions support this curated event set:

Event Type Meaning
registered An attendee registered.
joined An attendee joined the webinar.
left An attendee left the webinar.
no_show A registrant did not attend.
video_complete An attendee completed the video.
cta_click An attendee clicked an offer or CTA.

For poll responses, chat messages, adaptive branch events, or watch-time thresholds, use dashboard automation webhooks.

Send a POST request to:

POST /api/v1/webinars/{webinarId}/webhooks

Example:

Terminal window
curl -X POST https://app.webinarstack.co/api/v1/webinars/{webinarId}/webhooks \
-H "Authorization: Bearer wsk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: create-webhook-001" \
-d '{
"url": "https://example.com/webhooks/webinarstack",
"eventTypes": ["registered", "joined", "video_complete"],
"active": true
}'

The response includes a secret once. Store it securely.

{
"data": {
"id": "subscription_id",
"webinarId": "webinar_uuid",
"url": "https://example.com/webhooks/webinarstack",
"eventTypes": ["joined", "registered", "video_complete"],
"active": true,
"secret": "generated_signing_secret",
"createdAt": "2026-08-13T18:00:00.000Z"
}
}

The secret is not returned again in list responses. Use an idempotency key when creating a subscription so a safe retry can return the same creation response.

API subscription URLs are validated server-side:

  • The URL must be valid.
  • The URL must use https.
  • The hostname must resolve.
  • The hostname must not resolve to private, loopback, or link-local IP ranges.

For local testing, use a public HTTPS tunnel such as ngrok or Cloudflare Tunnel.

Action Method And Path Notes
List subscriptions GET /api/v1/webinars/{webinarId}/webhooks Does not return full secrets.
Get one subscription GET /api/v1/webinars/{webinarId}/webhooks/{webhookId} Returns secretLast4 and secretRotatedAt.
Update subscription PATCH /api/v1/webinars/{webinarId}/webhooks/{webhookId} Update url, eventTypes, or active. At least one field is required.
Delete subscription DELETE /api/v1/webinars/{webinarId}/webhooks/{webhookId} Deletes all rule rows that share the subscription ID.
Rotate secret POST /api/v1/webinars/{webinarId}/webhooks/{webhookId}/rotate-secret Returns the new secret once and immediately invalidates the old one.
List deliveries GET /api/v1/webinars/{webinarId}/webhooks/{webhookId}/deliveries Cursor-paginated delivery history.
Get delivery GET /api/v1/webinars/{webinarId}/webhooks/{webhookId}/deliveries/{deliveryId} Includes delivery status and response details.
Redeliver POST /api/v1/webinars/{webinarId}/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver Queues a new delivery and returns 202 Accepted.

Secret rotation has no grace window. Update your receiver with the new secret before the next webhook sends.

WebinarStack sends outbound webhooks as HTTP POST requests.

Headers:

Header Meaning
Content-Type: application/json The request body is JSON.
X-WebinarStack-Signature: sha256=<hex> HMAC-SHA256 signature of the exact raw request body.
User-Agent: WebinarStack/1.0 Identifies WebinarStack as the sender.

The signature is calculated as:

HMAC-SHA256(raw_request_body, webhook_secret)

The secret itself is not sent in the request. Your receiver stores the secret and uses it to verify the signature header.

Outbound webhook payloads use a common envelope:

{
"event_type": "registered",
"attendee": {
"name": "Jane Attendee",
"email": "jane@example.com",
"registration_id": "registration_uuid",
"utm_source": "newsletter",
"utm_medium": "email",
"utm_campaign": "launch"
},
"webinar": {
"id": "webinar_uuid",
"title": "How to Scale Your Funnel"
},
"event_data": {},
"timestamp": "2026-08-13T18:00:00.000Z"
}

Fields:

Field Meaning
event_type The automation trigger that fired.
attendee.name Attendee name from registration.
attendee.email Attendee email from registration.
attendee.registration_id WebinarStack registration ID.
attendee.utm_source UTM source from registration, or null.
attendee.utm_medium UTM medium from registration, or null.
attendee.utm_campaign UTM campaign from registration, or null.
webinar.id Webinar ID.
webinar.title Internal webinar title.
event_data Trigger-specific details. Shape varies by trigger.
timestamp Time when the outbound webhook payload was built.
is_refire Present and true when a user manually re-fires or redelivers a webhook.

Build your receiver to accept new fields without failing. WebinarStack may add fields over time.

The event_data object depends on the trigger.

Common examples:

Trigger Example event_data Fields
registered Usually empty.
joined Usually empty.
left Event payload fields from the attendee event, when available.
no_show detectedAt, sessionStartedAt.
watched_minutes watchedSeconds, completionPercent, optionally in_transition.
watched_percent watchedSeconds, completionPercent, optionally in_transition.
cta_click interactionId or offerId; may include interaction.id, interaction.headline, interaction.internal_name.
poll_response interactionId, optionId, selectedOption; may include interaction.id, interaction.headline, interaction.internal_name.
chat_response message, sent_at, message_event_id.
branch_taken branch_id, segment_id.
segment_completed segment_id, watch_duration_seconds.

Treat event_data as optional and event-specific. Do not require a poll field on a registration event, or an interaction field on a join event.

{
"event_type": "cta_click",
"attendee": {
"name": "Jane Attendee",
"email": "jane@example.com",
"registration_id": "registration_uuid",
"utm_source": null,
"utm_medium": null,
"utm_campaign": null
},
"webinar": {
"id": "webinar_uuid",
"title": "Product Demo"
},
"event_data": {
"interactionId": "offer_uuid",
"interaction": {
"id": "offer_uuid",
"headline": "Start your trial",
"internal_name": "Trial offer - webinar midpoint"
}
},
"timestamp": "2026-08-13T18:00:00.000Z"
}
{
"event_type": "poll_response",
"attendee": {
"name": "Jane Attendee",
"email": "jane@example.com",
"registration_id": "registration_uuid",
"utm_source": "linkedin",
"utm_medium": "paid",
"utm_campaign": "demo"
},
"webinar": {
"id": "webinar_uuid",
"title": "Product Demo"
},
"event_data": {
"interactionId": "poll_uuid",
"optionId": "option_uuid",
"selectedOption": "I want to talk to sales",
"interaction": {
"id": "poll_uuid",
"headline": "What should we help you with?",
"internal_name": "Mid-webinar qualification poll"
}
},
"timestamp": "2026-08-13T18:00:00.000Z"
}
{
"event_type": "chat_response",
"attendee": {
"name": "Jane Attendee",
"email": "jane@example.com",
"registration_id": "registration_uuid",
"utm_source": null,
"utm_medium": null,
"utm_campaign": null
},
"webinar": {
"id": "webinar_uuid",
"title": "Product Demo"
},
"event_data": {
"message": "Can someone send me pricing?",
"sent_at": "2026-08-13T18:00:00.000Z",
"message_event_id": "event_uuid"
},
"timestamp": "2026-08-13T18:00:00.000Z"
}

Your receiver should do four things:

  1. Read the raw request body before parsing JSON.
  2. Verify X-WebinarStack-Signature.
  3. Return a 2xx response quickly.
  4. Process the event idempotently.

Do not verify the signature against JSON.stringify(req.body) after your framework has parsed the request. Signature verification must use the exact raw body bytes.

import crypto from 'node:crypto'
function verifySignature(rawBody: string, signatureHeader: string | null, secret: string) {
if (!signatureHeader) return false
const [scheme, signature] = signatureHeader.split('=', 2)
if (scheme !== 'sha256' || !signature) return false
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
const expectedBuffer = Buffer.from(expected, 'hex')
const actualBuffer = Buffer.from(signature, 'hex')
if (actualBuffer.length !== expectedBuffer.length) return false
return crypto.timingSafeEqual(actualBuffer, expectedBuffer)
}
export async function POST(request: Request) {
const rawBody = await request.text()
const signature = request.headers.get('x-webinarstack-signature')
const secret = process.env.WEBINARSTACK_WEBHOOK_SECRET
if (!secret) {
return new Response('missing webhook secret', { status: 500 })
}
if (!verifySignature(rawBody, signature, secret)) {
return new Response('invalid signature', { status: 401 })
}
const event = JSON.parse(rawBody) as {
event_type: string
attendee?: { email?: string; registration_id?: string }
webinar?: { id?: string; title?: string }
event_data?: Record<string, unknown>
is_refire?: boolean
}
// Do minimal validation, then queue durable work.
// Example: await queue.add('webinarstack-webhook', event)
return new Response('ok', { status: 200 })
}
import express from 'express'
import crypto from 'node:crypto'
const app = express()
app.post(
'/webhooks/webinarstack',
express.raw({ type: 'application/json' }),
(req, res) => {
const rawBody = req.body.toString('utf8')
const signatureHeader = req.header('x-webinarstack-signature')
const secret = process.env.WEBINARSTACK_WEBHOOK_SECRET
if (!secret || !signatureHeader) {
return res.status(401).send('invalid signature')
}
const [scheme, signature] = signatureHeader.split('=', 2)
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
const valid =
scheme === 'sha256' &&
signature &&
Buffer.from(signature, 'hex').length === Buffer.from(expected, 'hex').length &&
crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'))
if (!valid) {
return res.status(401).send('invalid signature')
}
const event = JSON.parse(rawBody)
// Queue or process the event.
return res.status(200).send('ok')
},
)
import hashlib
import hmac
import json
import os
from fastapi import FastAPI, Header, HTTPException, Request
app = FastAPI()
def verify_signature(raw_body: bytes, signature_header: str | None, secret: str) -> bool:
if not signature_header:
return False
try:
scheme, signature = signature_header.split("=", 1)
except ValueError:
return False
if scheme != "sha256":
return False
expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature, expected)
@app.post("/webhooks/webinarstack")
async def webinarstack_webhook(
request: Request,
x_webinarstack_signature: str | None = Header(default=None),
):
raw_body = await request.body()
secret = os.environ.get("WEBINARSTACK_WEBHOOK_SECRET")
if not secret or not verify_signature(raw_body, x_webinarstack_signature, secret):
raise HTTPException(status_code=401, detail="invalid signature")
event = json.loads(raw_body)
# Queue or process event.
return {"ok": True}

Webhook delivery is at-least-once. Your receiver should tolerate duplicates.

Duplicates can happen when:

  • WebinarStack retries after your endpoint times out.
  • WebinarStack retries after a non-2xx response.
  • A user manually re-fires or redelivers a webhook.
  • Your endpoint processes the event but fails before returning a 2xx response.

For dashboard automation webhooks, WebinarStack prevents the same automation rule from firing repeatedly for the same attendee and session in normal automated flows. Chat-response automations can be configured to fire once per session or on every matching message. Manual re-fire is intentionally a new delivery.

Because the outbound payload does not include a single universal delivery ID, choose an idempotency key that matches your workflow. Examples:

Workflow Suggested Receiver Idempotency Key
Registration sync webinar.id + event_type + attendee.registration_id
Offer click task webinar.id + event_type + attendee.registration_id + event_data.interactionId
Poll answer sync webinar.id + event_type + attendee.registration_id + event_data.interactionId + event_data.optionId
Chat question task webinar.id + event_type + attendee.registration_id + event_data.message_event_id
Manual re-fire audit Include is_refire or store a separate re-fire log entry.

If your downstream system supports upsert operations, prefer upsert over blind create.

Your endpoint should return a 2xx response within 10 seconds.

WebinarStack treats these as success:

  • 200 OK
  • 201 Created
  • 202 Accepted
  • 204 No Content
  • Any other 2xx response

WebinarStack treats these as failure:

  • Network errors
  • Timeouts
  • 4xx responses
  • 5xx responses
  • A final non-2xx response after redirects

Failed webhook deliveries are retried by the background worker. The delivery history records the status, response code, attempt count, delivered timestamp, and response body snippet. Response bodies are truncated, so return concise diagnostic text from your receiver.

Good receiver responses:

ok
{ "ok": true }

Useful failure responses:

missing email
{ "error": "unknown webinar id" }

Avoid returning large HTML error pages. They are harder to read in delivery history and may be truncated.

For dashboard automation webhooks:

  1. Open the webinar.
  2. Go to Automations.
  3. Edit the webhook rule.
  4. Scroll to Delivery History.

The delivery history shows:

  • Timestamp
  • Status
  • Response code
  • Attempt count
  • Response body details when available
  • Re-fire button for failed deliveries

The Re-fire button is available only for failed dashboard webhook deliveries. Re-fire queues a new webhook delivery and includes is_refire: true in the payload.

For API webhook subscriptions, use the delivery endpoints to list, inspect, and redeliver deliveries. API redelivery returns 202 Accepted with a new delivery ID, then the delivery proceeds asynchronously.

A request inspector such as Webhook.site can confirm that WebinarStack is reaching the destination URL. This is useful for initial setup.

Use request inspectors carefully:

  • Do not use them for production traffic.
  • Do not paste webhook secrets into public tools.
  • Do not send real attendee data unless your policies allow it.
  • Use them to inspect headers and shape, then switch to your real receiver.

You can simulate WebinarStack locally by signing a sample body with your webhook secret.

Create a sample body:

{
"event_type": "registered",
"attendee": {
"name": "Test User",
"email": "test@example.com",
"registration_id": "test-registration-id",
"utm_source": null,
"utm_medium": null,
"utm_campaign": null
},
"webinar": {
"id": "test-webinar-id",
"title": "Test Webinar"
},
"event_data": {},
"timestamp": "2026-08-13T18:00:00.000Z"
}

Sign and send it with Node:

Terminal window
export WEBHOOK_SECRET="replace_with_your_secret"
export WEBHOOK_URL="https://example.com/webhooks/webinarstack"
export BODY='{"event_type":"registered","attendee":{"name":"Test User","email":"test@example.com","registration_id":"test-registration-id","utm_source":null,"utm_medium":null,"utm_campaign":null},"webinar":{"id":"test-webinar-id","title":"Test Webinar"},"event_data":{},"timestamp":"2026-08-13T18:00:00.000Z"}'
SIG=$(node -e "const crypto=require('node:crypto'); const body=process.env.BODY; const secret=process.env.WEBHOOK_SECRET; console.log(crypto.createHmac('sha256', secret).update(body).digest('hex'))")
curl -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-H "X-WebinarStack-Signature: sha256=$SIG" \
--data "$BODY"

The body you sign must exactly match the body you send.

After your receiver passes local tests:

  1. Create the webhook rule.
  2. Trigger the event with a test registration or test attendee session.
  3. Confirm your receiver logs the event.
  4. Confirm the signature verification passes.
  5. Confirm the receiver returns 2xx.
  6. Check Delivery History in the automation rule.

Common webhook mappings:

WebinarStack Field CRM Field
attendee.email Lead or contact email.
attendee.name Lead or contact name.
webinar.title Campaign, event, or source detail.
event_type Activity type or lifecycle event.
attendee.utm_source Lead source.
attendee.utm_campaign Campaign.
event_data.interaction.internal_name Offer or poll label.

Recommended pattern:

  1. Upsert the contact by email.
  2. Add or update webinar-specific fields.
  3. Append an activity record for the event.
  4. Create a task only for high-intent events, such as offer clicks or pricing chat messages.

Use webhooks for high-signal events, not every heartbeat or minor activity.

Good Slack alert examples:

  • Offer clicked.
  • Chat message contains pricing.
  • Watched at least 80 percent.
  • Took a high-intent adaptive branch.
  • No-show for a VIP invite list.

Use webhooks for one-way Slack alerts. If your team needs to reply to attendee chat from Slack threads, use the Slack Chat Integration.

For analytics pipelines:

  1. Verify the signature.
  2. Store the raw JSON payload.
  3. Store a normalized table with event_type, registration_id, webinar_id, attendee.email, timestamp, and relevant event_data fields.
  4. Use idempotent inserts or merge operations.
  5. Preserve unknown fields for future compatibility.

Many no-code tools can receive webhooks, but not all of them can verify HMAC signatures.

If your no-code tool cannot verify X-WebinarStack-Signature, use a small secure bridge:

  1. WebinarStack sends to your bridge endpoint.
  2. The bridge verifies the signature.
  3. The bridge forwards safe, normalized data to Zapier, Make, Airtable, Sheets, or another tool.

This keeps the webhook secret out of tools that cannot protect or verify it properly.

For a no-code setup walkthrough, field mapping examples, Zapier troubleshooting, and AI prompts, see the Zapier Integration guide.

Revenue webhooks are inbound webhooks. They send purchase data into WebinarStack so revenue can be attributed to attendees, webinars, and offers.

Configure revenue webhooks in Settings > Revenue.

Revenue settings showing Generic HMAC and Stripe webhook endpoint setup cards

Your account will show its own endpoint host. Copy the URL displayed in your dashboard when configuring an external payment tool.

Use the Stripe revenue webhook when purchases happen in Stripe and you want WebinarStack analytics to show revenue.

High-level setup:

  1. In WebinarStack, open Settings > Revenue.
  2. Copy the Stripe endpoint URL.
  3. In Stripe, create a webhook endpoint using that URL.
  4. Select relevant Stripe events, such as checkout session completion, successful charges, paid invoices, refunds, and disputes.
  5. Copy Stripe’s whsec_... signing secret.
  6. Paste that signing secret back into WebinarStack.
  7. Send a Stripe test event.
  8. Confirm the Last purchase received timestamp updates.

Stripe owns the signing secret for Stripe webhooks. Copy it from Stripe into WebinarStack.

Use Generic HMAC for a custom checkout or non-Stripe payment provider.

Your provider posts JSON to:

POST /api/webhooks/revenue/{accountId}/generic

Payload:

{
"event_id": "provider_unique_event_id",
"email": "buyer@example.com",
"amount_minor": 12000,
"currency": "USD",
"type": "purchase",
"original_transaction_id": null,
"timestamp": "2026-08-13T18:00:00.000Z"
}

Generic revenue requests must include:

X-WebinarStack-Signature: sha256=<hex>

where <hex> is HMAC-SHA256(raw_body, generic_revenue_signing_secret).

For refunds and chargebacks, amount_minor should be zero or negative. Use original_transaction_id to pair the event to the original purchase when possible.

Before going live:

  • Use HTTPS for production webhook destinations.
  • Store webhook secrets in environment variables or a secret manager.
  • Never hard-code webhook secrets in source code.
  • Never paste real webhook secrets into public AI chats, issue trackers, or support tickets.
  • Verify X-WebinarStack-Signature against the raw request body.
  • Use constant-time comparison for HMAC checks.
  • Return 2xx quickly and do slow work asynchronously.
  • Make downstream actions idempotent.
  • Log enough context to debug, but avoid logging full attendee payloads unless your privacy policy allows it.
  • Treat event_data as trigger-specific and forward-compatible.
  • Rotate secrets if a secret is exposed.

Check:

  • Did your endpoint return a non-2xx status?
  • Did it take longer than 10 seconds?
  • Did your framework reject the request before your handler ran?
  • Is the URL correct?
  • Is the endpoint public and reachable?
  • Does the response body contain an error message?
  • Did the receiver expect a field that is not present for that trigger?

Fix the receiver first, then re-fire or redeliver the webhook.

Common causes:

  • You copied the wrong secret.
  • You rotated the secret but did not update the receiver.
  • You verified JSON.stringify(parsedBody) instead of the raw body.
  • Middleware parsed or modified the body before verification.
  • You included sha256= in the HMAC input instead of only using it as the header prefix.
  • Your comparison expects base64, but WebinarStack sends hex.

Add idempotency in your receiver. Use a key based on the attendee, webinar, event type, and trigger-specific IDs.

For manual re-fires, decide whether to update the existing record, create an audit record, or ignore the duplicate.

Check:

  • The automation rule is enabled.
  • The webinar event actually happened.
  • The trigger type matches the event.
  • Conditions are not too restrictive.
  • Before-session triggers are not being used on a JIT-only webinar.
  • For adaptive branch triggers, the webinar is adaptive and the selected branch or segment exists.
  • For API subscriptions, the subscription is active and includes the desired event type.

A No-Code Tool Receives The Event But Cannot Verify It

Section titled “A No-Code Tool Receives The Event But Cannot Verify It”

Use a verified bridge endpoint. Ask an engineer or an AI assistant to create a small endpoint that verifies the signature, transforms the payload, and forwards safe fields to the no-code tool.

Your favorite AI assistant can help you build and debug webhook integrations quickly. The safest pattern is to describe the contract, paste redacted payload samples, and use placeholders for secrets.

Do not paste real webhook secrets into AI tools. Use placeholders such as WEBINARSTACK_WEBHOOK_SECRET or replace_with_secret_from_env.

I need a webhook receiver for WebinarStack in [framework/language].
Webhook contract:
- Method: POST
- Body: JSON
- Signature header: X-WebinarStack-Signature: sha256=<hex>
- Signature algorithm: HMAC-SHA256(raw request body, WEBINARSTACK_WEBHOOK_SECRET)
- The secret must come from an environment variable, not source code.
- The handler must verify the signature before parsing or trusting the JSON.
- It should return 2xx quickly and queue slow work asynchronously.
Payload envelope:
{
"event_type": "registered",
"attendee": {
"name": "Jane Attendee",
"email": "jane@example.com",
"registration_id": "registration_uuid",
"utm_source": null,
"utm_medium": null,
"utm_campaign": null
},
"webinar": {
"id": "webinar_uuid",
"title": "Product Demo"
},
"event_data": {},
"timestamp": "2026-08-13T18:00:00.000Z"
}
Please write production-quality code, explain how raw body handling works in this framework, and include a small test that proves invalid signatures are rejected.
I want to map WebinarStack webhook payloads into [CRM/tool name].
Here are the events I care about:
- registered
- joined
- cta_click
- poll_response
- chat_response
Here is the payload envelope and example event_data:
[paste redacted payload examples here]
Please design a field mapping for contacts, campaign membership, activities, and sales tasks. Use attendee.email as the primary contact lookup key. Make the design idempotent so webhook retries do not create duplicate records.
Write a local test script that sends a signed WebinarStack-style webhook request to my development endpoint.
Requirements:
- Language: [Node.js/Python/etc.]
- Read WEBINARSTACK_WEBHOOK_SECRET and WEBHOOK_URL from environment variables.
- Build a JSON payload for event_type "registered".
- Sign the exact raw JSON string with HMAC-SHA256.
- Send X-WebinarStack-Signature as sha256=<hex>.
- Print the HTTP status and response body.
- Do not hard-code secrets.
Help me debug a failed WebinarStack webhook delivery.
Facts:
- Destination URL: [redacted host/path]
- Event type: [event_type]
- HTTP response status in WebinarStack: [status]
- Response body shown in delivery history: [paste response body]
- Receiver framework: [framework]
- I verify X-WebinarStack-Signature using HMAC-SHA256 over the raw request body.
Please give me the most likely causes, what logs to add, and a step-by-step test plan. Do not ask me to paste the real webhook secret.
Please review this webhook signature verification code for security and correctness.
Webhook contract:
- Header: X-WebinarStack-Signature: sha256=<hex>
- Algorithm: HMAC-SHA256(raw request body, secret)
- The secret is stored in WEBINARSTACK_WEBHOOK_SECRET.
Check specifically for:
- Raw body handling before JSON parsing
- Constant-time comparison
- Correct hex decoding
- Rejecting missing or malformed headers
- Avoiding secret logging
- Returning 401 on invalid signatures
Here is my code:
[paste code with secrets removed]
I need to connect WebinarStack webhooks to [Zapier/Make/Airtable/Google Sheets], but the destination cannot verify HMAC signatures.
Design a secure bridge endpoint that:
- Receives WebinarStack webhook POSTs
- Verifies X-WebinarStack-Signature using WEBINARSTACK_WEBHOOK_SECRET
- Normalizes the payload to the fields my no-code tool needs
- Forwards only non-secret fields to the no-code webhook URL
- Returns 2xx quickly
- Retries forwarding safely or queues failures
Please include deployment recommendations for [Vercel/Cloudflare Workers/AWS Lambda/etc.] and explain where to store secrets.

Prompt: Plan A Revenue Webhook Integration

Section titled “Prompt: Plan A Revenue Webhook Integration”
I want to send purchase data into WebinarStack using the Generic HMAC revenue webhook.
Contract:
- Method: POST
- URL: /api/webhooks/revenue/{accountId}/generic
- Header: X-WebinarStack-Signature: sha256=<hex>
- Signature: HMAC-SHA256(raw body, generic revenue signing secret)
- JSON fields:
- event_id: unique provider event ID
- email: buyer email
- amount_minor: integer minor units
- currency: 3-letter currency code
- type: purchase, refund, or chargeback
- original_transaction_id: original purchase ID for refund or chargeback, nullable
- timestamp: ISO datetime
Please help me map [payment provider/custom checkout] events into this payload, including how to make event_id idempotent and how to sign the exact raw body.
Create a runbook for our team to operate WebinarStack webhooks.
Include:
- Where the receiver is deployed
- Which WebinarStack rules or API subscriptions send to it
- Which environment variable stores the secret
- How to rotate the secret
- How to test with a signed sample payload
- How to read WebinarStack delivery history
- What to do for failed deliveries
- How to avoid duplicate downstream records
Do not include real secrets. Use placeholders.
  1. Destination URL is public and HTTPS.
  2. Receiver verifies X-WebinarStack-Signature.
  3. Secret is stored outside source code.
  4. Receiver handles duplicate deliveries.
  5. Receiver returns 2xx within 10 seconds.
  6. Receiver logs enough to debug failures.
  7. A signed local test passes.
  8. A real WebinarStack test event reaches the receiver.
  9. Delivery History shows success.
  10. Your team has a rotation and failure runbook.