Extract the first hub ID

What you will build

In this tutorial, you will create a complete single-session virtual event for "CloudSync Pro Launch" a product unveiling webinar scheduled for December 15, 2025, at 2:00 PM EST. You will set up registration, configure branding, pre-register VIP attendees, and publish the event to make it live.

Event Details

  • Product: CloudSync Pro - Next-generation cloud storage solution
  • Date: December 15, 2025, 2:00 PM - 3:00 PM EST
  • Format: Virtual webinar (up to 10,000 attendees)
  • Registration: Public with email confirmation
  • Duration: 1 hour live presentation + Q&A

Time required

20—30 minutes

What you will learn

  • Create a SIMPLEEVENT (_single-session) event.
  • Configure webinar-style presentation settings.
  • Set up public registration with authentication.
  • Pre-register VIP attendees via API.
  • Publish and activate your event.

Prerequisites

Before starting, ensure you have:

  • Account: Zoom Pro or higher account plan
  • License: Zoom Webinars Plus or Events license
  • API Access: Server-to-Server OAuth app or OAuth app with these scopes:
    • zoom_events:write:event (or zoom_events_basic:write:admin)
    • zoom_events:write:access_links (or zoom_events_access_links:write:admin)
    • zoom_events:write:ticket (or zoom_events_tickets:write:admin)
  • Access Token: Valid OAuth access token (referenced as YOUR_ACCESS_TOKEN below)

No API credentials? Visit the Zoom Marketplace to create an app.


Step 1: Get your Hub ID

Every Zoom Events event must belong to an event hub. Retrieve your hub ID.

cURL

curl -X GET "https://api.zoom.us/v2/zoom_events/hubs?role_type=host" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Python

import requests
url = "https://api.zoom.us/v2/zoom_events/hubs"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
params = {
    "role_type": "host"
}
response = requests.get(url, headers=headers, params=params)
response.raise_for_status()  # Check for HTTP errors
hubs = response.json()
# Extract the first hub ID
hub_id = hubs["hubs"][0]["hub_id"]
print(f"Hub ID: {hub_id}")

JavaScript (Node.js)

const axios = require("axios");
async function getHubId() {
    const url = "https://api.zoom.us/v2/zoom_events/hubs";
    const headers = {
        Authorization: "Bearer YOUR_ACCESS_TOKEN",
    };
    const params = {
        role_type: "host",
    };
    try {
        const response = await axios.get(url, { headers, params });
        const hubId = response.data.hubs[0].hub_id;
        console.log(`Hub ID: ${hubId}`);
        return hubId;
    } catch (error) {
        console.error(
            "Failed to get hub ID:",
            error.response?.data || error.message,
        );
        throw error;
    }
}
getHubId();

Expected Response

{
    "total_records": 1,
    "hubs": [
        {
            "hub_id": "aBcDeFgH1234567890",
            "name": "My Events Hub",
            "hub_active": true
        }
    ]
}

Save this hub_id - You will need it in the next step.


Step 2: Create the product launch event

Create the CloudSync Pro Launch event as a single-session webinar.

cURL

curl -X POST "https://api.zoom.us/v2/zoom_events/events" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -d '{
    "hub_id": "aBcDeFgH1234567890",
    "name": "CloudSync Pro Launch",
    "": "Join us for the exclusive launch of CloudSync Pro, our next-generation cloud storage solution. Discover breakthrough features, live demos, and special launch pricing available only to attendees.",
    "timezone": "America/New_York",
    "event_type": "SIMPLE_EVENT",
    "access_level": "PRIVATE_UNRESTRICTED",
    "meeting_type": "WEBINAR",
    "attendance_type": "VIRTUAL",
    "tagline": "The Future of Cloud Storage Starts Here",
    "contact_name": "Sarah Chen",
    "calendar": [
      {
        "start_time": "2025-12-15T19:00:00Z",
        "end_time": "2025-12-15T20:00:00Z"
      }
    ]
  }'

Python

import requests
from datetime import datetime, timezone
url = "https://api.zoom.us/v2/zoom_events/events"
headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
event_data = {
    "hub_id": "aBcDeFgH1234567890",
    "name": "CloudSync Pro Launch",
    "": "Join us for the exclusive launch of CloudSync Pro, our next-generation cloud storage solution. Discover breakthrough features, live demos, and special launch pricing available only to attendees.",
    "timezone": "America/New_York",
    "event_type": "SIMPLE_EVENT",
    "access_level": "PRIVATE_UNRESTRICTED",
    "meeting_type": "WEBINAR",
    "attendance_type": "VIRTUAL",
    "tagline": "The Future of Cloud Storage Starts Here",
    "contact_name": "Sarah Chen",
    "calendar": [
        {
            "start_time": "2025-12-15T19:00:00Z",  # 2 PM EST = 7 PM UTC
            "end_time": "2025-12-15T20:00:00Z"      # 3 PM EST = 8 PM UTC
        }
    ]
}
response = requests.post(url, headers=headers, json=event_data)
response.raise_for_status()  # Check for HTTP errors
event = response.json()
event_id = event["event_id"]
print(f"Event created. Event ID: {event_id}")
print(f"Status: {event['status']}")

JavaScript (Node.js)

const axios = require('axios');
async function createEvent(hubId) {
  const url = 'https://api.zoom.us/v2/zoom_events/events';
  const headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_ACCESS_TOKEN'
  };
  const eventData = {
    hub_id: hubId,
    name: 'CloudSync Pro Launch',
    : 'Join us for the exclusive launch of CloudSync Pro, our next-generation cloud storage solution. Discover breakthrough features, live demos, and special launch pricing available only to attendees.',
    timezone: 'America/New_York',
    event_type: 'SIMPLE_EVENT',
    access_level: 'PRIVATE_UNRESTRICTED',
    meeting_type: 'WEBINAR',
    attendance_type: 'VIRTUAL',
    tagline: 'The Future of Cloud Storage Starts Here',
    contact_name: 'Sarah Chen',
    calendar: [
      {
        start_time: '2025-12-15T19:00:00Z',  // 2 PM EST = 7 PM UTC
        end_time: '2025-12-15T20:00:00Z'      // 3 PM EST = 8 PM UTC
      }
    ]
  };
  try {
    const response = await axios.post(url, eventData, { headers });
    const event = response.data;
    console.log(`Event created. Event ID: ${event.event_id}`);
    console.log(`Status: ${event.status}`);
    return event.event_id;
  } catch (error) {
    console.error('Failed to create event:', error.response?.data || error.message);
    throw error;
  }
}
createEvent('aBcDeFgH1234567890');

Expected Response

{
    "event_id": "kNqLPC6hSFiZ9NpgjA549w",
    "name": "CloudSync Pro Launch",
    "": "Join us for the exclusive launch of CloudSync Pro...",
    "timezone": "America/New_York",
    "event_type": "SIMPLE_EVENT",
    "status": "draft",
    "meeting_type": "WEBINAR",
    "access_level": "PRIVATE_UNRESTRICTED",
    "attendance_type": "VIRTUAL",
    "hub_id": "aBcDeFgH1234567890",
    "calendar": [
        {
            "start_time": "2025-12-15T19:00:00Z",
            "end_time": "2025-12-15T20:00:00Z"
        }
    ],
    "tagline": "The Future of Cloud Storage Starts Here",
    "contact_name": "Sarah Chen"
}

Important Notes

  • The event starts in draft status (not yet live)
  • event_type: "SIMPLE_EVENT" is immutable - cannot be changed after creation.
  • meeting_type: "WEBINAR" supports up to 100,000 attendees (host/panelist video only).
  • Times must be in UTC format (yyyy-MM-ddTHH:mm:ssZ).

Save the event_id - You will use it throughout the rest of the tutorial.


Step 3: Retrieve the default Ticket Type

Zoom Events automatically creates a "General Admission" ticket type when you create an event. Retrieve its ID.

cURL

curl -X GET "https://api.zoom.us/v2/zoom_events/events/kNqLPC6hSFiZ9NpgjA549w/ticket_types" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Python

import requests
event_id = "kNqLPC6hSFiZ9NpgjA549w"
url = f"https://api.zoom.us/v2/zoom_events/events/{event_id}/ticket_types"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
response = requests.get(url, headers=headers)
response.raise_for_status()  # Check for HTTP errors
ticket_types = response.json()
# Get the first (default) ticket type
ticket_type_id = ticket_types["ticket_types"][0]["ticket_type_id"]
print(f"Ticket Type ID: {ticket_type_id}")
print(f"Name: {ticket_types['ticket_types'][0]['name']}")

JavaScript (Node.js)

const axios = require("axios");
async function getTicketTypeId(eventId) {
    const url = `https://api.zoom.us/v2/zoom_events/events/${eventId}/ticket_types`;
    const headers = {
        Authorization: "Bearer YOUR_ACCESS_TOKEN",
    };
    try {
        const response = await axios.get(url, { headers });
        const ticketTypeId = response.data.ticket_types[0].ticket_type_id;
        console.log(`Ticket Type ID: ${ticketTypeId}`);
        console.log(`Name: ${response.data.ticket_types[0].name}`);
        return ticketTypeId;
    } catch (error) {
        console.error(
            "Failed to get ticket type:",
            error.response?.data || error.message,
        );
        throw error;
    }
}
getTicketTypeId("kNqLPC6hSFiZ9NpgjA549w");

Expected Response

{
    "total_records": 1,
    "ticket_types": [
        {
            "ticket_type_id": "234kjhg23kl4jhlaksjdh3",
            "name": "General Admission",
            "currency": "USD",
            "free": true,
            "price": "0",
            "quantity": 50
        }
    ]
}

Save the ticket_type_id - You will use it when creating registration links and tickets.


Step 4: Create a public registration link

Create a registration link that anyone can use to sign up for the CloudSync Pro Launch.

cURL

curl -X POST "https://api.zoom.us/v2/zoom_events/events/kNqLPC6hSFiZ9NpgjA549w/access_links" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -d '{
    "name": "Public Registration",
    "type": "registration",
    "is_default": true,
    "authentication_method": "bypass_auth",
    "ticket_type_id": "234kjhg23kl4jhlaksjdh3",
    "security_at_join": {
      "email_authentication": false,
      "security_code_verification": false
    }
  }'

Python

import requests
event_id = "kNqLPC6hSFiZ9NpgjA549w"
ticket_type_id = "234kjhg23kl4jhlaksjdh3"
url = f"https://api.zoom.us/v2/zoom_events/events/{event_id}/access_links"
headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
link_data = {
    "name": "Public Registration",
    "type": "registration",
    "is_default": True,
    "authentication_method": "bypass_auth",
    "ticket_type_id": ticket_type_id,
    "security_at_join": {
        "email_authentication": False,
        "security_code_verification": False
    }
}
response = requests.post(url, headers=headers, json=link_data)
response.raise_for_status()  # Check for HTTP errors
access_link = response.json()
print(f"Registration link created")
print(f"Link ID: {access_link['access_link_id']}")
print(f"Type: {access_link['type']}")

JavaScript (Node.js)

const axios = require("axios");
async function createRegistrationLink(eventId, ticketTypeId) {
    const url = `https://api.zoom.us/v2/zoom_events/events/${eventId}/access_links`;
    const headers = {
        "Content-Type": "application/json",
        Authorization: "Bearer YOUR_ACCESS_TOKEN",
    };
    const linkData = {
        name: "Public Registration",
        type: "registration",
        is_default: true,
        authentication_method: "bypass_auth",
        ticket_type_id: ticketTypeId,
        security_at_join: {
            email_authentication: false,
            security_code_verification: false,
        },
    };
    try {
        const response = await axios.post(url, linkData, { headers });
        const accessLink = response.data;
        console.log("Registration link created");
        console.log(`Link ID: ${accessLink.access_link_id}`);
        console.log(`Type: ${accessLink.type}`);
        return accessLink.access_link_id;
    } catch (error) {
        console.error(
            "Failed to create registration link:",
            error.response?.data || error.message,
        );
        throw error;
    }
}
createRegistrationLink("kNqLPC6hSFiZ9NpgjA549w", "234kjhg23kl4jhlaksjdh3");

Expected response

{
    "access_link_id": "xyz789abc456",
    "name": "Public Registration",
    "type": "registration",
    "is_default": true,
    "authentication_method": "bypass_auth",
    "ticket_type_id": "234kjhg23kl4jhlaksjdh3",
    "security_at_join": {
        "email_authentication": false,
        "security_code_verification": false
    }
}

What this does

  • type: "registration" - Attendees must register before joining.
  • authentication_method: "bypass_auth" - No login required (public event).
  • is_default: true - This link is the primary registration path.
  • security_at_join.email_authentication: false - No email authentication required at join time.
  • security_at_join.security_code_verification: false - No security code required at join time.

Alternative authentication methods

  • zoom_account - Require Zoom account sign-in
  • zoom_account_otp - Zoom account or email OTP
  • corporate_idp - Enterprise SSO (single sign-on)

Step 5: Pre-register VIP attendees (optional)

For the CloudSync Pro Launch, pre-register three VIP guests who should receive direct invitations.

cURL

curl -X POST "https://api.zoom.us/v2/zoom_events/events/kNqLPC6hSFiZ9NpgjA549w/tickets" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -d '{
    "tickets": [
      {
        "email": "alex.rivera@techcorp.com",
        "first_name": "Alex",
        "last_name": "Rivera",
        "ticket_type_id": "234kjhg23kl4jhlaksjdh3",
        "send_notification": true,
        "fast_join": false
      },
      {
        "email": "jordan.patel@innovate.io",
        "first_name": "Jordan",
        "last_name": "Patel",
        "ticket_type_id": "234kjhg23kl4jhlaksjdh3",
        "send_notification": true,
        "fast_join": false
      },
      {
        "email": "taylor.kim@cloudsys.com",
        "first_name": "Taylor",
        "last_name": "Kim",
        "ticket_type_id": "234kjhg23kl4jhlaksjdh3",
        "send_notification": true,
        "fast_join": false
      }
    ]
  }'

Python

import requests
event_id = "kNqLPC6hSFiZ9NpgjA549w"
ticket_type_id = "234kjhg23kl4jhlaksjdh3"
url = f"https://api.zoom.us/v2/zoom_events/events/{event_id}/tickets"
headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
# VIP attendee list
tickets_data = {
    "tickets": [
        {
            "email": "alex.rivera@techcorp.com",
            "first_name": "Alex",
            "last_name": "Rivera",
            "ticket_type_id": ticket_type_id,
            "send_notification": True,
            "fast_join": False
        },
        {
            "email": "jordan.patel@innovate.io",
            "first_name": "Jordan",
            "last_name": "Patel",
            "ticket_type_id": ticket_type_id,
            "send_notification": True,
            "fast_join": False
        },
        {
            "email": "taylor.kim@cloudsys.com",
            "first_name": "Taylor",
            "last_name": "Kim",
            "ticket_type_id": ticket_type_id,
            "send_notification": True,
            "fast_join": False
        }
    ]
}
response = requests.post(url, headers=headers, json=tickets_data)
response.raise_for_status()  # Check for HTTP errors
result = response.json()
print(f"Pre-registered {len(result['tickets'])} VIP attendees")
for ticket in result['tickets']:
    print(f"  - {ticket['email']}: {ticket.get('event_join_link', 'N/A')}")

JavaScript (Node.js)

const axios = require("axios");
async function preRegisterVIPs(eventId, ticketTypeId) {
    const url = `https://api.zoom.us/v2/zoom_events/events/${eventId}/tickets`;
    const headers = {
        "Content-Type": "application/json",
        Authorization: "Bearer YOUR_ACCESS_TOKEN",
    };
    const ticketsData = {
        tickets: [
            {
                email: "alex.rivera@techcorp.com",
                first_name: "Alex",
                last_name: "Rivera",
                ticket_type_id: ticketTypeId,
                send_notification: true,
                fast_join: false,
            },
            {
                email: "jordan.patel@innovate.io",
                first_name: "Jordan",
                last_name: "Patel",
                ticket_type_id: ticketTypeId,
                send_notification: true,
                fast_join: false,
            },
            {
                email: "taylor.kim@cloudsys.com",
                first_name: "Taylor",
                last_name: "Kim",
                ticket_type_id: ticketTypeId,
                send_notification: true,
                fast_join: false,
            },
        ],
    };
    try {
        const response = await axios.post(url, ticketsData, { headers });
        const result = response.data;
        console.log(`Pre-registered ${result.tickets.length} VIP attendees`);
        result.tickets.forEach((ticket) => {
            console.log(
                `  - ${ticket.email}: ${ticket.event_join_link || "N/A"}`,
            );
        });
    } catch (error) {
        console.error(
            "Failed to pre-register VIPs:",
            error.response?.data || error.message,
        );
        throw error;
    }
}
preRegisterVIPs("kNqLPC6hSFiZ9NpgjA549w", "234kjhg23kl4jhlaksjdh3");

Expected response

{
    "errors": [],
    "tickets": [
        {
            "ticket_id": "ticket_abc123",
            "email": "alex.rivera@techcorp.com",
            "first_name": "Alex",
            "last_name": "Rivera",
            "event_join_link": "https://events.zoom.us/j/xyz123abc"
        },
        {
            "ticket_id": "ticket_def456",
            "email": "jordan.patel@innovate.io",
            "first_name": "Jordan",
            "last_name": "Patel",
            "event_join_link": "https://events.zoom.us/j/xyz456def"
        },
        {
            "ticket_id": "ticket_ghi789",
            "email": "taylor.kim@cloudsys.com",
            "first_name": "Taylor",
            "last_name": "Kim",
            "event_join_link": "https://events.zoom.us/j/xyz789ghi"
        }
    ]
}

What this does

  • Creates tickets (registrations) for each VIP attendee.
  • send_notification: true - Each attendee receives confirmation email with join link.
  • fast_join: false - Attendees must verify email before joining (recommended for security).
  • Returns unique event_join_link for each attendee.

Batch limits

You can pre-register up to 30 attendees per API call.


Step 6: Publish the event

Now it is time to make your CloudSync Pro Launch event live. Publishing activates all registration links and makes the event discoverable.

cURL

curl -X POST "https://api.zoom.us/v2/zoom_events/events/kNqLPC6hSFiZ9NpgjA549w/event_actions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -d '{
    "operation": "publish"
  }'

Python

import requests
event_id = "kNqLPC6hSFiZ9NpgjA549w"
url = f"https://api.zoom.us/v2/zoom_events/events/{event_id}/event_actions"
headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
publish_data = {
    "operation": "publish"
}
response = requests.post(url, headers=headers, json=publish_data)
response.raise_for_status()  # Check for HTTP errors
result = response.json()
print(f"Event published successfully")
print(f"Status: {result['status']}")

JavaScript (Node.js)

const axios = require("axios");
async function publishEvent(eventId) {
    const url = `https://api.zoom.us/v2/zoom_events/events/${eventId}/event_actions`;
    const headers = {
        "Content-Type": "application/json",
        Authorization: "Bearer YOUR_ACCESS_TOKEN",
    };
    const publishData = {
        operation: "publish",
    };
    try {
        const response = await axios.post(url, publishData, { headers });
        const result = response.data;
        console.log("Event published successfully");
        console.log(`Status: ${result.status}`);
    } catch (error) {
        console.error(
            "Failed to publish event:",
            error.response?.data || error.message,
        );
        throw error;
    }
}
publishEvent("kNqLPC6hSFiZ9NpgjA549w");

Expected Response

{
    "status": "PUBLISHED",
    "event_id": "kNqLPC6hSFiZ9NpgjA549w"
}

Results when you publish:

  • Event status changes from draft to published.
  • All registration links become active.
  • Event appears in your hub (if hub is public).
  • Confirmation emails are sent to pre-registered attendees.
  • The event is now accessible to registrants.

Important: After publishing, certain fields become immutable (e.g., meeting_type, event_type).


Step 7: Verify your event

Confirm everything is set up correctly by retrieving the event details.

cURL

curl -X GET "https://api.zoom.us/v2/zoom_events/events/kNqLPC6hSFiZ9NpgjA549w" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Python

import requests
event_id = "kNqLPC6hSFiZ9NpgjA549w"
url = f"https://api.zoom.us/v2/zoom_events/events/{event_id}"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
response = requests.get(url, headers=headers)
response.raise_for_status()  # Check for HTTP errors
event = response.json()
print("=" * 60)
print("CloudSync Pro Launch Event Summary")
print("=" * 60)
print(f"Event Name: {event['name']}")
print(f"Status: {event['status']}")
print(f"Event Type: {event['event_type']}")
print(f"Meeting Type: {event['meeting_type']}")
print(f"Start Time: {event['calendar'][0]['start_time']}")
print(f"Registration URL: {event['event_url']}")
print(f"Tagline: {event.get('tagline', 'N/A')}")
print("=" * 60)

JavaScript (Node.js)

const axios = require("axios");
async function verifyEvent(eventId) {
    const url = `https://api.zoom.us/v2/zoom_events/events/${eventId}`;
    const headers = {
        Authorization: "Bearer YOUR_ACCESS_TOKEN",
    };
    try {
        const response = await axios.get(url, { headers });
        const event = response.data;
        console.log("=".repeat(60));
        console.log("CloudSync Pro Launch Event Summary");
        console.log("=".repeat(60));
        console.log(`Event Name: ${event.name}`);
        console.log(`Status: ${event.status}`);
        console.log(`Event Type: ${event.event_type}`);
        console.log(`Meeting Type: ${event.meeting_type}`);
        console.log(`Start Time: ${event.calendar[0].start_time}`);
        console.log(`Registration URL: ${event.event_url}`);
        console.log(`Tagline: ${event.tagline || "N/A"}`);
        console.log("=".repeat(60));
    } catch (error) {
        console.error(
            "Failed to verify event:",
            error.response?.data || error.message,
        );
        throw error;
    }
}
verifyEvent("kNqLPC6hSFiZ9NpgjA549w");

Expected Output

============================================================
CloudSync Pro Launch Event Summary
============================================================
Event Name: CloudSync Pro Launch
Status: PUBLISHED
Event Type: SIMPLE_EVENT
Meeting Type: WEBINAR
Start Time: 2025-12-15T19:00:00Z
Registration URL: https://zoom.us/events/abc123xyz
Tagline: The Future of Cloud Storage Starts Here
============================================================

Verification checklist

  • Status is published
  • event_url is accessible (share this link for registration)
  • Start/end times are correct
  • Event details match your specifications

What you built

Congratulations! You have successfully created a complete single-session virtual event. Here is what you accomplished:

Event configuration

  • Name: CloudSync Pro Launch
  • Type: Single-session webinar (SIMPLE_EVENT)
  • Capacity: Up to 100,000 attendees
  • Date: December 15, 2025, 2:00 PM - 3:00 PM EST
  • Status: Published and live

Registration setup

  • Public registration link (no login required)
  • Default "General Admission" ticket type
  • Pre-registered 3 VIP attendees with confirmation emails
  • Active registration URL ready to share

Next steps

Now that your event is live, you can:

  1. Share the registration link

    • Use event_url from the event response
    • Embed on your website or share via email/social media
  2. Add branding (optional)

    • Upload event logo and banner images
    • Customize colors and themes via the Zoom web portal
  3. Configure session settings

    • Enable Q&A, polls, chat, reactions
    • Set up breakout rooms for networking
    • Configure recording settings
  4. Add speakers/panelists

    • Use POST /zoom_events/events/{eventId}/speakers endpoint
    • Speakers appear on event page and in lobby
  5. Monitor registrations

    • Use GET /zoom_events/events/{eventId}/registrants to view attendees
    • Export registration data for analysis
  6. Send reminder emails

    • Zoom automatically sends reminders 1 day and 1 hour before
    • Customize email templates via the web portal

Configuration options

Meeting vs. Webinar Mode

FeatureMEETINGWEBINAR
Max Capacity1,000 attendees100,000 attendees
Video SharingAll participantsHost/panelists only
Screen SharingAll participantsHost/panelists only
Best ForWorkshops, roundtablesPresentations, broadcasts

When to use meeting: Interactive sessions where attendees need video/audio. When to use webinar: Large presentations where only hosts present.

Access levels

  • PRIVATE_UNRESTRICTED - Anyone can register via link (no approval needed)
  • PRIVATE_RESTRICTED - Host must approve each registration

Attendance types

  • VIRTUAL - Online-only event (default)
  • IN-PERSON - Physical venue only
  • HYBRID - Both virtual and in-person attendees

For hybrid events, add physical_location field and enable check-in features.


Troubleshooting

Common errors

Error 26501: Event not published

Problem: Trying to create tickets before publishing event. Solution: Publish event first using Step 6, then create tickets.

Error 261202: Ticket type not found

Problem: Invalid ticket_type_id in request. Solution: Retrieve valid ticket type ID using Step 3.

Error 260200: Event access denied

Problem: Insufficient permissions or wrong hub. Solution: Verify API scopes and ensure you have host access to the hub.

Error 26202: Event schedule cannot be more than 6 days

Problem: Single-session events have a 6-day maximum duration. Solution: Reduce session length or use CONFERENCE type for multi-session events.

Error 261203: Duplicate access link name

Problem: An access link with this name already exists. Solution: Use a unique name for each registration link.


API Reference quick links


Complete example code

Python full workflow

import requests
from datetime import datetime, timezone
# Configuration
BASE_URL = "https://api.zoom.us/v2/zoom_events"
ACCESS_TOKEN = "YOUR_ACCESS_TOKEN"
HEADERS = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {ACCESS_TOKEN}"
}
def create_product_launch_event():
    # Step 1: Get Hub ID
    hubs_response = requests.get(
        f"{BASE_URL}/hubs",
        headers={"Authorization": f"Bearer {ACCESS_TOKEN}"},
        params={"role_type": "host"}
    )
    hubs_response.raise_for_status()  # Check for HTTP errors
    hub_id = hubs_response.json()["hubs"][0]["hub_id"]
    print(f" Hub ID: {hub_id}")
    # Step 2: Create Event
    event_data = {
        "hub_id": hub_id,
        "name": "CloudSync Pro Launch",
        "": "Join us for the exclusive launch of CloudSync Pro...
        "timezone": "America/New_York",
        "event_type": "SIMPLE_EVENT",
        "access_level": "PRIVATE_UNRESTRICTED",
        "meeting_type": "WEBINAR",
        "attendance_type": "VIRTUAL",
        "tagline": "The Future of Cloud Storage Starts Here",
        "contact_name": "Sarah Chen",
        "calendar": [
            {
                "start_time": "2025-12-15T19:00:00Z",
                "end_time": "2025-12-15T20:00:00Z"
            }
        ]
    }
    event_response = requests.post(f"{BASE_URL}/events", headers=HEADERS, json=event_data)
    event_response.raise_for_status()  # Check for HTTP errors
    event_id = event_response.json()["event_id"]
    print(f" Event created: {event_id}")
    # Step 3: Get Ticket Type
    ticket_types_response = requests.get(
        f"{BASE_URL}/events/{event_id}/ticket_types",
        headers={"Authorization": f"Bearer {ACCESS_TOKEN}"}
    )
    ticket_types_response.raise_for_status()  # Check for HTTP errors
    ticket_type_id = ticket_types_response.json()["ticket_types"][0]["ticket_type_id"]
    print(f" Ticket Type ID: {ticket_type_id}")
    # Step 4: Create Registration Link
    link_data = {
        "name": "Public Registration",
        "type": "registration",
        "is_default": True,
        "authentication_method": "bypass_auth",
        "ticket_type_id": ticket_type_id,
        "security_at_join": {
            "email_authentication": False,
            "security_code_verification": False
        }
    }
    link_response = requests.post(
        f"{BASE_URL}/events/{event_id}/access_links",
        headers=HEADERS,
        json=link_data
    )
    link_response.raise_for_status()  # Check for HTTP errors
    print(f" Registration link created")
    # Step 5: Pre-register VIPs
    tickets_data = {
        "tickets": [
            {
                "email": "alex.rivera@techcorp.com",
                "first_name": "Alex",
                "last_name": "Rivera",
                "ticket_type_id": ticket_type_id,
                "send_notification": True,
                "fast_join": False
            },
            {
                "email": "jordan.patel@innovate.io",
                "first_name": "Jordan",
                "last_name": "Patel",
                "ticket_type_id": ticket_type_id,
                "send_notification": True,
                "fast_join": False
            }
        ]
    }
    tickets_response = requests.post(
        f"{BASE_URL}/events/{event_id}/tickets",
        headers=HEADERS,
        json=tickets_data
    )
    tickets_response.raise_for_status()  # Check for HTTP errors
    print(f" Pre-registered {len(tickets_response.json()['tickets'])} VIPs")
    # Step 6: Publish Event
    publish_response = requests.post(
        f"{BASE_URL}/events/{event_id}/event_actions",
        headers=HEADERS,
        json={"operation": "publish"}
    )
    publish_response.raise_for_status()  # Check for HTTP errors
    print(f" Event published")
    # Step 7: Get Event URL
    event_details_response = requests.get(
        f"{BASE_URL}/events/{event_id}",
        headers={"Authorization": f"Bearer {ACCESS_TOKEN}"}
    )
    event_details_response.raise_for_status()  # Check for HTTP errors
    event_details = event_details_response.json()
    print("\n" + "="*60)
    print("EVENT CREATED SUCCESSFULLY")
    print("="*60)
    print(f"Event Name: {event_details['name']}")
    print(f"Registration URL: {event_details['event_url']}")
    print(f"Start Time: {event_details['calendar'][0]['start_time']}")
    print("="*60)
if __name__ == "__main__":
    create_product_launch_event()

Tutorial complete

You now know how to create, configure, and publish a single-session virtual event using the Zoom Webinars Plus & Events API.

Questions or issues? Check the Troubleshooting section or visit the Zoom Developer Forum.