> ## Documentation Index
> Fetch the complete documentation index at: https://flywheel.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Webinars API

> Create webinars, register attendees, and drive reminder + follow-up workflows

## Overview

The Webinars API lets you run your entire webinar communication flow through Flywheel:

1. **Create a webinar** (or create it in the dashboard) and link it to a workflow
2. **Register people** as they sign up — registrants don't need to exist in Flywheel first; profiles are created or matched automatically
3. Flywheel **enrolls each registrant** into every live workflow with a matching **Webinar Registration** trigger (reminder emails/SMS anchored to the start time, etc.)
4. **Report attendance** during or after the webinar so **Webinar Attendance Split** nodes can branch attendees vs no-shows for follow-up
5. **Reschedule** if the date moves — all pending anchored reminders automatically recompute to the new time

## Authentication

All webinar endpoints require authentication using an API key and authentication type header. Include both headers in your requests:

```
Authorization: YOUR_API_KEY
Auth-Type: api
```

API keys are managed in the Flywheel dashboard under **Settings → API Keys**. All webinar objects are scoped to the organization the API key belongs to.

## Create a Webinar

`POST /v1/webinars`

```javascript theme={null}
const response = await fetch('https://api.flywheel.cx/v1/webinars', {
  method: 'POST',
  headers: {
    'Authorization': 'YOUR_API_KEY',
    'Auth-Type': 'api',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    webinar: {
      title: 'Onboarding Masterclass',
      start_time: '2026-09-01T17:00:00Z',
      timezone: 'America/New_York',
      duration_minutes: 60,
      join_url: 'https://zoom.us/j/123456789',
      provider: 'zoom',
      automation_id: 'a3a9c0de-...' // optional: workflow to associate
    }
  })
});

const { webinar } = await response.json();
// webinar.id → use this for registrations
```

| Field              | Type   | Required | Description                                      |
| ------------------ | ------ | -------- | ------------------------------------------------ |
| `title`            | string | Yes      | Webinar title                                    |
| `start_time`       | string | Yes      | ISO 8601 datetime with timezone offset           |
| `timezone`         | string | No       | IANA timezone, e.g. `America/New_York`           |
| `duration_minutes` | number | No       | Length of the webinar                            |
| `join_url`         | string | No       | Link attendees use to join                       |
| `provider`         | string | No       | Platform name, e.g. `zoom`, `livestorm`          |
| `status`           | string | No       | `draft` or `scheduled` (defaults to `scheduled`) |
| `automation_id`    | string | No       | Workflow to associate with this webinar          |

<Note>
  Webinars can also be created in the dashboard under **Webinars**. Dashboard-created webinars start as drafts; API-created webinars default to `scheduled`.
</Note>

## Register a User

`POST /v1/webinars/register`

The core endpoint. Pass whatever you know about the person — **they do not need to be an existing Flywheel user**. Flywheel matches them to an existing profile by `user_id`, `email`, `phone` or `anonymous_id`, or creates a new user profile if there's no match.

```javascript theme={null}
const response = await fetch('https://api.flywheel.cx/v1/webinars/register', {
  method: 'POST',
  headers: {
    'Authorization': 'YOUR_API_KEY',
    'Auth-Type': 'api',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    webinar_id: '0d4f6f36-2b6a-4c1e-9a6f-1f2d3c4b5a69',
    user: {
      email: 'jane@acme.com',
      full_name: 'Jane Doe',
      phone: '+15550123456',
      properties: {
        company: 'Acme',
        signup_source: 'landing_page'
      }
    }
  })
});

const { registration_id, org_user_id, already_registered } = await response.json();
```

### What happens on registration

1. **User identification** — the user is found (by `user_id` → `email` → `phone` → `anonymous_id` priority) or created. Name fields and custom `properties` are merged into their profile.
2. **Registration** — an idempotent registration record is created for the (webinar, user) pair.
3. **Workflow enrollment** — the user is enrolled into every live workflow with a **Webinar Registration** trigger matching this webinar (or bound to "any webinar").
4. **Event fired** — a `$fw_webinar_registration` event is recorded on the user's timeline with the webinar details, usable in event-triggered workflows, segments, and analytics.

<Note>
  Registering the same user for the same webinar twice is safe: the API returns the existing registration with `already_registered: true` and fires **no** duplicate workflows or events.
</Note>

### User object

| Field                      | Type   | Description                                                    |
| -------------------------- | ------ | -------------------------------------------------------------- |
| `user_id`                  | string | Your own id for this person (`org_assigned_user_id`)           |
| `email`                    | string | Email address                                                  |
| `phone`                    | string | Phone number                                                   |
| `first_name` / `last_name` | string | Name fields                                                    |
| `full_name`                | string | Split into first/last automatically when those aren't supplied |
| `anonymous_id`             | string | Anonymous id from your tracking                                |
| `properties`               | object | Custom profile properties (string, number, boolean values)     |

At least one of `user_id`, `email`, `phone` or `anonymous_id` is required.

### The `$fw_webinar_registration` event

Every new registration records this event with the following properties:

```json theme={null}
{
  "event_name": "$fw_webinar_registration",
  "custom_properties": {
    "webinar_id": "0d4f6f36-...",
    "webinar_title": "Onboarding Masterclass",
    "webinar_start_time": "2026-09-01T17:00:00.000Z",
    "webinar_join_url": "https://zoom.us/j/123456789",
    "registration_id": "7c1b2a3d-..."
  }
}
```

The `$fw_` prefix is reserved — it can only be produced by Flywheel itself, so the event is a trustworthy signal in workflows and segments.

## Webinar Platform Integrations (Demio)

If your organization has connected **Demio** (Dashboard → Settings → Integrations) and the webinar is linked to a Demio event in the webinar drawer, registration does two extra things automatically:

1. The registrant is **pushed to the Demio event**, so they exist in Demio without any extra call.
2. Demio's **personalized join link** for that attendee is stored on the registration and exposed to workflows as the `registration_join_url` context variable — use it in reminder emails/SMS so each person gets their own join button.

Attendance also syncs automatically: about 30 minutes after the webinar's scheduled end, Flywheel pulls Demio's participants report and marks attendees, feeding Webinar Attendance Split workflow nodes with no manual attendance calls. You can still call the attendance endpoint below for corrections or non-Demio webinars.

## Mark Attendance

`POST /v1/webinars/attendance`

Report that a registrant attended (or didn't). Resolve the user by `user_id` or `email`. Attendance is read **live** by Webinar Attendance Split workflow nodes when they run, so you can report it during the webinar or after it ends.

```javascript theme={null}
await fetch('https://api.flywheel.cx/v1/webinars/attendance', {
  method: 'POST',
  headers: {
    'Authorization': 'YOUR_API_KEY',
    'Auth-Type': 'api',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    webinar_id: '0d4f6f36-2b6a-4c1e-9a6f-1f2d3c4b5a69',
    email: 'jane@acme.com',
    attended: true // optional, defaults to true
  })
});
```

## Reschedule a Webinar

`POST /v1/webinars/reschedule`

Move the start time. Every pending "wait until webinar start ± offset" step in in-flight workflow runs is recomputed, so reminder sequences follow the new time automatically.

```javascript theme={null}
const response = await fetch('https://api.flywheel.cx/v1/webinars/reschedule', {
  method: 'POST',
  headers: {
    'Authorization': 'YOUR_API_KEY',
    'Auth-Type': 'api',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    webinar_id: '0d4f6f36-2b6a-4c1e-9a6f-1f2d3c4b5a69',
    start_time: '2026-09-08T17:00:00Z'
  })
});

const { recomputed } = await response.json();
// recomputed → number of pending reminder tasks moved to the new time
```

## Typical Integration

A landing-page signup form wired to Flywheel:

```javascript theme={null}
// 1. Once: create the webinar (or grab its id from the dashboard)
const { webinar } = await flywheel('/v1/webinars', {
  webinar: { title: 'Product Deep Dive', start_time: '2026-09-15T16:00:00Z' }
});

// 2. On every form submission: register the signup
app.post('/webinar-signup', async (req, res) => {
  await flywheel('/v1/webinars/register', {
    webinar_id: webinar.id,
    user: {
      email: req.body.email,
      full_name: req.body.name,
      properties: { signup_source: 'landing_page' }
    }
  });
  res.redirect('/thanks');
});

// 3. After the webinar: sync attendance from your platform (e.g. Zoom report)
for (const attendee of zoomAttendees) {
  await flywheel('/v1/webinars/attendance', {
    webinar_id: webinar.id,
    email: attendee.email
  });
}
```

Pair this with a workflow that uses the **Webinar Registration** trigger, anchored waits ("1 day before start", "1 hour before start"), and a **Webinar Attendance Split** after the webinar ends for attendee vs no-show follow-ups.
