Skip to content

Webhooks

Send registrations, attendance, offer clicks, poll answers and questions to Zapier, Make or your own system, with signed requests and automatic retries.

Updated View as Markdown

A webhook sends a message to a web address the moment something happens in your webinar. Use one to pass data to Zapier, Make, a CRM that isn’t listed or your own system. Webhooks carry more detail than automation rules, including personal join links and answers to your registration questions.

The Webhooks page

Endpoints belong to the workspace, so each one receives events from every webinar and challenge you run. There’s no filter by webinar, but each message says which webinar it’s about, so you can filter at the other end.

Add an endpoint

Get a URL

Get the address that will receive the events. In Zapier, that’s a Webhooks by Zapier trigger with Catch Hook. In Make, it’s a Custom webhook module. For your own system, it’s whichever address you’ve built.

Add it to FullHouse

Go to Workspace → Webhooks and click Add endpoint. Paste the address into Endpoint URL.

Choose your events

All the events under Events are ticked to start with. Untick those you don’t need.

Save and copy the secret

Click Add endpoint. A Signing secret appears once at the top of the page. Copy it now and store it safely. You’ll need it to verify requests.

Rules for the URL

The address must start with https:// and be reachable from the public internet. It can be up to 500 characters and can include a query string, but no username, password or # part.

Message What to do
Enter a valid HTTPS URL. Check that it’s a complete address starting with https://.
Use a public HTTPS URL without credentials or a fragment. Remove any user:password@ or # part. Local and internal addresses aren’t allowed.
Choose at least one supported event. Tick at least one event.

Manage endpoints

Each endpoint on the page shows its URL, number of events, when it was added, how many deliveries have failed, and a chip for each event.

  • Switch to pause or resume it. While it’s paused, no new deliveries are created, so events that happen in that time are never sent. A delivery that was waiting to retry when you paused it isn’t sent either.
  • Trash icon to delete it, after you confirm “Remove this endpoint?”. Its delivery history goes with it.

You can’t change an endpoint’s URL or events, or see or replace its secret, once it’s created. Delete it and add a new one, which gets a new secret, and update your receiving system.

A paused endpoint gets no deliveries. A delivery that falls due while it’s paused is marked failed, without using an attempt, with “This endpoint is paused. Turn it back on, then retry.” Turning the endpoint back on doesn’t resend these automatically, so retry each one from Debug.

Recent deliveries at the bottom of the page shows the latest 20 across all your endpoints, with their status and the response your endpoint gave. To retry a failure, use Debug.

Status Meaning
pending Waiting to send, or waiting to retry.
success Your endpoint replied with a 2xx status.
failed All three attempts failed, or the endpoint was paused when it was due. It stays this way unless you retry it from Debug.

Events

Event When it fires How often
registrant.created Someone new registers, including through an embed or Pinball. A challenge sign-up sends this once, for Day 1. Adding someone on the Registrants tab sends it too. Deleting a registrant sends nothing. Once per registration. Registering again with the same email doesn’t send another.
attendee.joined Someone enters the live room for the first time. Once per registrant
attendee.watched_percent Someone’s live watch time passes 10%, 25%, 50%, 75%, 90% or 100%. Once per milestone at most. If two milestones pass at once, one event is sent, for the higher one.
offer.seen An offer is shown to someone, live or on replay. Once for the live room and once for replay, per registrant. It doesn’t fire again for a second offer.
offer.clicked Someone clicks an offer, live or on replay. Every click, so expect repeats
poll.answered Someone answers a poll. Every answer, including when they change it
question.asked Someone sends a question from the Q&A tab. Chat messages don’t fire this. Every question
replay.viewed Someone opens the replay for the first time. Once per registrant

What each message looks like

Every message is a POST request with a JSON body and three parts at the top level: event, created_at (when it happened, in UTC) and data.

Fields in every event

Field What it holds
data.webinar_id The webinar’s number. For a challenge, the day’s webinar.
data.webinar_title Its title.
data.session_id The session’s number.
data.session_starts_at When the session starts, in UTC.
data.registrant.id The registrant’s number.
data.registrant.email Their email address.
data.registrant.first_name Their first name.
data.registrant.last_name Their last name, or null.
data.registrant.phone Their phone number, or null.
data.registrant.timezone Their time zone, or null.
data.registrant.custom Answers to registration questions, or null. Each key is a question’s ID, holding the question and answer. Unanswered questions are left out.
data.utm UTM values from registration, or null. Keys are source, medium, campaign, term and content, and only those with a value appear.
data.join_link The person’s private link to the room.
data.replay_link Their private link to the replay.
data.attended Whether they’ve entered the live room.
data.watch_seconds Seconds watched in the live room so far.
data.percent_watched The same as a percentage of the webinar, up to 100. Sent as a decimal, such as 50.0.
data.clicked_offer Whether they’ve clicked an offer in the live room.

Watch figures only count the live room, so someone watching the replay can show 0.

registrant.created

No extra fields. The person hasn’t watched anything yet.

{
  "created_at": "2026-06-09T09:12:44+00:00",
  "data": {
    "attended": false,
    "clicked_offer": false,
    "join_link": "https://showtell.live/w/scaling-webinars/join/3f9a1c7e5b2d4f60a8c1e7d39b4f2a6c",
    "percent_watched": 0.0,
    "registrant": {
      "custom": {
        "q_7d1f0c2ab34e": {
          "answer": "Under 10 people",
          "question": "How big is your team?"
        }
      },
      "email": "[email protected]",
      "first_name": "Ada",
      "id": 48213,
      "last_name": "Lovelace",
      "phone": "+44 7700 900123",
      "timezone": "Europe/London"
    },
    "replay_link": "https://showtell.live/w/scaling-webinars/replay/3f9a1c7e5b2d4f60a8c1e7d39b4f2a6c",
    "session_id": 905,
    "session_starts_at": "2026-06-10T18:00:00+00:00",
    "utm": {
      "campaign": "june-launch",
      "medium": "email",
      "source": "newsletter"
    },
    "watch_seconds": 0,
    "webinar_id": 12,
    "webinar_title": "Scaling with evergreen webinars"
  },
  "event": "registrant.created"
}

attendee.joined

Adds data.join_offset_seconds, how far into the webinar they were when they arrived.

{
  "created_at": "2026-06-10T18:01:12+00:00",
  "data": {
    "attended": true,
    "clicked_offer": false,
    "join_link": "https://showtell.live/w/scaling-webinars/join/3f9a1c7e5b2d4f60a8c1e7d39b4f2a6c",
    "join_offset_seconds": 72,
    "percent_watched": 0.0,
    "registrant": {
      "custom": {
        "q_7d1f0c2ab34e": {
          "answer": "Under 10 people",
          "question": "How big is your team?"
        }
      },
      "email": "[email protected]",
      "first_name": "Ada",
      "id": 48213,
      "last_name": "Lovelace",
      "phone": "+44 7700 900123",
      "timezone": "Europe/London"
    },
    "replay_link": "https://showtell.live/w/scaling-webinars/replay/3f9a1c7e5b2d4f60a8c1e7d39b4f2a6c",
    "session_id": 905,
    "session_starts_at": "2026-06-10T18:00:00+00:00",
    "utm": {
      "campaign": "june-launch",
      "medium": "email",
      "source": "newsletter"
    },
    "watch_seconds": 0,
    "webinar_id": 12,
    "webinar_title": "Scaling with evergreen webinars"
  },
  "event": "attendee.joined"
}

attendee.watched_percent

Adds data.milestone, the milestone passed: 10, 25, 50, 75, 90 or 100.

{
  "created_at": "2026-06-10T18:30:40+00:00",
  "data": {
    "attended": true,
    "clicked_offer": false,
    "join_link": "https://showtell.live/w/scaling-webinars/join/3f9a1c7e5b2d4f60a8c1e7d39b4f2a6c",
    "milestone": 50,
    "percent_watched": 50.0,
    "registrant": {
      "custom": {
        "q_7d1f0c2ab34e": {
          "answer": "Under 10 people",
          "question": "How big is your team?"
        }
      },
      "email": "[email protected]",
      "first_name": "Ada",
      "id": 48213,
      "last_name": "Lovelace",
      "phone": "+44 7700 900123",
      "timezone": "Europe/London"
    },
    "replay_link": "https://showtell.live/w/scaling-webinars/replay/3f9a1c7e5b2d4f60a8c1e7d39b4f2a6c",
    "session_id": 905,
    "session_starts_at": "2026-06-10T18:00:00+00:00",
    "utm": {
      "campaign": "june-launch",
      "medium": "email",
      "source": "newsletter"
    },
    "watch_seconds": 1800,
    "webinar_id": 12,
    "webinar_title": "Scaling with evergreen webinars"
  },
  "event": "attendee.watched_percent"
}

offer.seen

Adds data.offer, with the offer’s id and headline.

{
  "created_at": "2026-06-10T18:42:00+00:00",
  "data": {
    "attended": true,
    "clicked_offer": false,
    "join_link": "https://showtell.live/w/scaling-webinars/join/3f9a1c7e5b2d4f60a8c1e7d39b4f2a6c",
    "offer": {
      "headline": "Join the Evergreen Accelerator",
      "id": 3307
    },
    "percent_watched": 67.0,
    "registrant": {
      "custom": {
        "q_7d1f0c2ab34e": {
          "answer": "Under 10 people",
          "question": "How big is your team?"
        }
      },
      "email": "[email protected]",
      "first_name": "Ada",
      "id": 48213,
      "last_name": "Lovelace",
      "phone": "+44 7700 900123",
      "timezone": "Europe/London"
    },
    "replay_link": "https://showtell.live/w/scaling-webinars/replay/3f9a1c7e5b2d4f60a8c1e7d39b4f2a6c",
    "session_id": 905,
    "session_starts_at": "2026-06-10T18:00:00+00:00",
    "utm": {
      "campaign": "june-launch",
      "medium": "email",
      "source": "newsletter"
    },
    "watch_seconds": 2400,
    "webinar_id": 12,
    "webinar_title": "Scaling with evergreen webinars"
  },
  "event": "offer.seen"
}

offer.clicked

Adds data.offer, with the offer’s id, headline and url.

{
  "created_at": "2026-06-10T18:44:31+00:00",
  "data": {
    "attended": true,
    "clicked_offer": true,
    "join_link": "https://showtell.live/w/scaling-webinars/join/3f9a1c7e5b2d4f60a8c1e7d39b4f2a6c",
    "offer": {
      "headline": "Join the Evergreen Accelerator",
      "id": 3307,
      "url": "https://yoursite.com/accelerator"
    },
    "percent_watched": 71.0,
    "registrant": {
      "custom": {
        "q_7d1f0c2ab34e": {
          "answer": "Under 10 people",
          "question": "How big is your team?"
        }
      },
      "email": "[email protected]",
      "first_name": "Ada",
      "id": 48213,
      "last_name": "Lovelace",
      "phone": "+44 7700 900123",
      "timezone": "Europe/London"
    },
    "replay_link": "https://showtell.live/w/scaling-webinars/replay/3f9a1c7e5b2d4f60a8c1e7d39b4f2a6c",
    "session_id": 905,
    "session_starts_at": "2026-06-10T18:00:00+00:00",
    "utm": {
      "campaign": "june-launch",
      "medium": "email",
      "source": "newsletter"
    },
    "watch_seconds": 2550,
    "webinar_id": 12,
    "webinar_title": "Scaling with evergreen webinars"
  },
  "event": "offer.clicked"
}

poll.answered

Adds data.poll, with its id, question and the chosen answer.

{
  "created_at": "2026-06-10T18:15:03+00:00",
  "data": {
    "attended": true,
    "clicked_offer": false,
    "join_link": "https://showtell.live/w/scaling-webinars/join/3f9a1c7e5b2d4f60a8c1e7d39b4f2a6c",
    "percent_watched": 25.0,
    "poll": {
      "answer": "Webinars",
      "id": 3299,
      "question": "Where do most of your leads come from?"
    },
    "registrant": {
      "custom": {
        "q_7d1f0c2ab34e": {
          "answer": "Under 10 people",
          "question": "How big is your team?"
        }
      },
      "email": "[email protected]",
      "first_name": "Ada",
      "id": 48213,
      "last_name": "Lovelace",
      "phone": "+44 7700 900123",
      "timezone": "Europe/London"
    },
    "replay_link": "https://showtell.live/w/scaling-webinars/replay/3f9a1c7e5b2d4f60a8c1e7d39b4f2a6c",
    "session_id": 905,
    "session_starts_at": "2026-06-10T18:00:00+00:00",
    "utm": {
      "campaign": "june-launch",
      "medium": "email",
      "source": "newsletter"
    },
    "watch_seconds": 900,
    "webinar_id": 12,
    "webinar_title": "Scaling with evergreen webinars"
  },
  "event": "poll.answered"
}

question.asked

Adds data.question, the text they sent.

{
  "created_at": "2026-06-10T18:20:18+00:00",
  "data": {
    "attended": true,
    "clicked_offer": false,
    "join_link": "https://showtell.live/w/scaling-webinars/join/3f9a1c7e5b2d4f60a8c1e7d39b4f2a6c",
    "percent_watched": 33.0,
    "question": "Is there a payment plan?",
    "registrant": {
      "custom": {
        "q_7d1f0c2ab34e": {
          "answer": "Under 10 people",
          "question": "How big is your team?"
        }
      },
      "email": "[email protected]",
      "first_name": "Ada",
      "id": 48213,
      "last_name": "Lovelace",
      "phone": "+44 7700 900123",
      "timezone": "Europe/London"
    },
    "replay_link": "https://showtell.live/w/scaling-webinars/replay/3f9a1c7e5b2d4f60a8c1e7d39b4f2a6c",
    "session_id": 905,
    "session_starts_at": "2026-06-10T18:00:00+00:00",
    "utm": {
      "campaign": "june-launch",
      "medium": "email",
      "source": "newsletter"
    },
    "watch_seconds": 1200,
    "webinar_id": 12,
    "webinar_title": "Scaling with evergreen webinars"
  },
  "event": "question.asked"
}

replay.viewed

No extra fields.

{
  "created_at": "2026-06-11T08:05:09+00:00",
  "data": {
    "attended": false,
    "clicked_offer": false,
    "join_link": "https://showtell.live/w/scaling-webinars/join/3f9a1c7e5b2d4f60a8c1e7d39b4f2a6c",
    "percent_watched": 0.0,
    "registrant": {
      "custom": {
        "q_7d1f0c2ab34e": {
          "answer": "Under 10 people",
          "question": "How big is your team?"
        }
      },
      "email": "[email protected]",
      "first_name": "Ada",
      "id": 48213,
      "last_name": "Lovelace",
      "phone": "+44 7700 900123",
      "timezone": "Europe/London"
    },
    "replay_link": "https://showtell.live/w/scaling-webinars/replay/3f9a1c7e5b2d4f60a8c1e7d39b4f2a6c",
    "session_id": 905,
    "session_starts_at": "2026-06-10T18:00:00+00:00",
    "utm": {
      "campaign": "june-launch",
      "medium": "email",
      "source": "newsletter"
    },
    "watch_seconds": 0,
    "webinar_id": 12,
    "webinar_title": "Scaling with evergreen webinars"
  },
  "event": "replay.viewed"
}

Headers

Every request includes these headers:

Header What it holds
Content-Type application/json
X-FullHouse-Event The event name, such as registrant.created.
X-FullHouse-Delivery A number for this delivery. It stays the same on every retry, so use it to spot repeats.
X-FullHouse-Signature The signature, so you can check the message is genuine.

Verify requests

If you’ve built your own endpoint, check the signature before trusting a message. Otherwise, anyone who finds your URL could send you fake registrations. Tools like Zapier and Make don’t usually need this, because their URLs are long and hard to guess.

X-FullHouse-Signature is an HMAC-SHA256 of the raw request body, using your signing secret as the key, written as lowercase hex. There’s no prefix or timestamp.

Always work from the exact bytes received. Parsing the JSON and converting it back to text can change it, and the signature won’t match.

import crypto from 'node:crypto';
import express from 'express';

const app = express();
const secret = process.env.FULLHOUSE_WEBHOOK_SECRET;

// express.raw keeps the body exactly as it arrived.
app.post('/fullhouse', express.raw({ type: 'application/json' }), (req, res) => {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(req.body)
    .digest('hex');
  const received = req.get('X-FullHouse-Signature') ?? '';

  const valid =
    received.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));

  if (!valid) {
    return res.status(401).send('Invalid signature');
  }

  // Reply first, then do any slow work. You have 10 seconds.
  res.sendStatus(200);

  const event = JSON.parse(req.body);
  const delivery = req.get('X-FullHouse-Delivery');
  console.log(delivery, event.event, event.data.registrant.email);
});

app.listen(3000);

Since the signature has no timestamp, a captured request stays valid. If that matters to you, keep the X-FullHouse-Delivery numbers you’ve handled and ignore repeats.

Retries

An attempt succeeds if your endpoint replies with a 2xx status within 10 seconds. Anything else counts as a failure, including a redirect, because FullHouse doesn’t follow them. It allows 5 seconds of that to connect.

Attempt When
1st As soon as the event happens
2nd About 30 seconds after the first fails
3rd About 5 minutes after the second fails

After the third failure, the delivery is marked failed and shows under Failed webhook deliveries in Debug, where you can click Retry to send it again with a fresh three attempts. A retried message has its original contents, including its original created_at. There’s no automatic retry after that, and an endpoint isn’t switched off for failing.

So your system should:

  • Reply fast. Return a 2xx straight away, and do slow work afterwards.
  • Expect repeats. A message can arrive twice, for example if your reply gets lost. Use X-FullHouse-Delivery to ignore repeats.
  • Not rely on order. Messages can arrive out of order, particularly after a retry. Use created_at if the order matters.
Error What it means
Webhook returned HTTP 500. Your endpoint replied with that status. Check your system.
Webhook request failed. FullHouse couldn’t connect or didn’t get a reply in time, the address no longer points to a public location, or the reply was over 1 MB.

Test an endpoint

There’s no test button. To send a real event:

  1. Add your endpoint. If you want a look at the messages first, use Zapier’s Catch Hook, or any request-inspection tool, as a temporary URL.
  2. Publish a webinar, or use one that’s already live, and register with a new email address, such as [email protected]. That sends registrant.created.
  3. Check Recent deliveries on the Webhooks page for the status.
  4. For other events, join the session and watch, answer a poll, ask a question and click the offer.

Registering again with the same email doesn’t send another registrant.created, so use a new address for each try.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close