# Exchange credentials for access token

<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

This tutorial shows you how to create and publish a Zoom Event by using the API. You will build a example scenario: a **Q4 company town hall** scheduled for December 15, 2025. Each section includes step-by-step guidance—from authentication through verification—and provides runnable code samples in multiple languages.

## What you will build

A published virtual event with registration enabled, ready to share with attendees.

## Time required

15&mdash;30 minutes

### Prerequisites

-   Pro+ Zoom account with Webinars Plus or Events license
-   OAuth credentials (Client ID and Secret) - see [Zoom's OAuth](/docs/integrations/oauth/)
-   Required OAuth scope: `zoom_events:write:event`
-   Basic command line knowledge

---

## The scenario: Q4 company town hall

You are organizing a virtual company-wide town hall meeting:

-   **Event name**: "Q4 Town Hall: Looking Ahead to 2026"
-   **Date**: December 15, 2025, 2:00 PM EST (7:00 PM UTC)
-   **Format**: Webinar-style (broadcast mode, up to 100K attendees)
-   **Access**: Private but unrestricted (anyone with link can register)

---

## Step 1: Get your access token

Before making any API calls, you need an OAuth access token. This token authorizes all subsequent requests.

### What this step does

Exchanging your OAuth credentials for a temporary access token.

### cURL

```shell
curl -X POST https://zoom.us/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET"
```

### Python

```python
import requests

# Exchange credentials for access token
token_url = "https://zoom.us/oauth/token"
client_id = "YOUR_CLIENT_ID"
client_secret = "YOUR_CLIENT_SECRET"

response = requests.post(
    token_url,
    data={
        "grant_type": "client_credentials"
    },
    auth=(client_id, client_secret)
)

token_data = response.json()
access_token = token_data["access_token"]
print(f"Access token obtained: {access_token[:20]}...")
```

### JavaScript (Node.js)

```javascript
const axios = require("axios");

// Exchange credentials for access token
const clientId = "YOUR_CLIENT_ID";
const clientSecret = "YOUR_CLIENT_SECRET";
const tokenUrl = "https://zoom.us/oauth/token";

async function getAccessToken() {
    const response = await axios.post(
        tokenUrl,
        "grant_type=client_credentials",
        {
            auth: {
                username: clientId,
                password: clientSecret,
            },
            headers: {
                "Content-Type": "application/x-www-form-urlencoded",
            },
        },
    );

    const accessToken = response.data.access_token;
    console.log(`Access token obtained: ${accessToken.substring(0, 20)}...`);
    return accessToken;
}

const token = await getAccessToken();
```

### Results

-   Zoom validates your credentials.
-   Returns an access token (valid for ~1 hour).
-   You will use this token in the `Authorization` header for all API calls.

**Save this token** - It is required for later steps.

---

## Step 2: Get your Hub ID

Every Webinars Plus & Events account has at least one "hub" - a container for your events. You need the hub ID to create events.

### What this step does

Retrieving the list of hubs you have access to and selecting one.

### cURL

```shell
# Replace YOUR_ACCESS_TOKEN with the token from Step 1
curl https://api.zoom.us/v2/zoom_events/hubs?role_type=host \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

### Python

```python
import requests

# Get list of hubs
hub_url = "https://api.zoom.us/v2/zoom_events/hubs"
headers = {
    "Authorization": f"Bearer {access_token}"
}
params = {
    "role_type": "host"
}

response = requests.get(hub_url, headers=headers, params=params)
hubs_data = response.json()

# Extract the first hub ID
hub_id = hubs_data["hubs"][0]["hub_id"]
print(f"Using hub ID: {hub_id}")
```

### JavaScript (Node.js)

```javascript
// Get list of hubs
async function getHubId(token) {
    const hubUrl = "https://api.zoom.us/v2/zoom_events/hubs";

    const response = await axios.get(hubUrl, {
        headers: {
            Authorization: `Bearer ${token}`,
        },
        params: {
            role_type: "host",
        },
    });

    // Extract the first hub ID
    const hubId = response.data.hubs[0].hub_id;
    console.log(`Using hub ID: ${hubId}`);
    return hubId;
}

const hubId = await getHubId(token);
```

### Example response

```json
{
    "total_records": 1,
    "hubs": [
        {
            "hub_id": "dKj3xR9bQYq5mNp8wA7cZf",
            "name": "Acme Corp Events Hub"
        }
    ]
}
```

### Results

-   Zoom returns all hubs where you are a host.
-   Most accounts have one default hub.
-   The `hub_id` will be used in the next step to create your event.

**Copy the hub_id** - Create your event.

---

## Step 3: Create the event in draft status

Create the event in **draft** status. This allows you to configure everything before making it public.

### What this step does

Creating a new event with basic configuration. The event starts in draft mode so you can review before publishing.

### cURL

```shell
# Create event (replace YOUR_ACCESS_TOKEN and YOUR_HUB_ID)
curl -X POST https://api.zoom.us/v2/zoom_events/events \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "hub_id": "YOUR_HUB_ID",
    "name": "Q4 Town Hall: Looking Ahead to 2026",
    "timezone": "America/New_York",
    "event_type": "SIMPLE_EVENT",
    "access_level": "PRIVATE_UNRESTRICTED",
    "meeting_type": "WEBINAR",
    "attendance_type": "VIRTUAL",
    "calendar": [
      {
        "start_time": "2025-12-15T19:00:00Z",
        "end_time": "2025-12-15T20:30:00Z"
      }
    ]
  }'
```

### Python

```python
import requests
from datetime import datetime, timezone

# Create event
event_url = "https://api.zoom.us/v2/zoom_events/events"
headers = {
    "Authorization": f"Bearer {access_token}",
    "Content-Type": "application/json"
}

# Event date: December 15, 2025, 2:00 PM EST (7:00 PM UTC)
start_time = datetime(2025, 12, 15, 19, 0, 0, tzinfo=timezone.utc)
end_time = datetime(2025, 12, 15, 20, 30, 0, tzinfo=timezone.utc)

event_data = {
    "hub_id": hub_id,  # From Step 2
    "name": "Q4 Town Hall: Looking Ahead to 2026",
    "timezone": "America/New_York",
    "event_type": "SIMPLE_EVENT",
    "access_level": "PRIVATE_UNRESTRICTED",
    "meeting_type": "WEBINAR",
    "attendance_type": "VIRTUAL",
    "calendar": [
        {
            "start_time": start_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
            "end_time": end_time.strftime("%Y-%m-%dT%H:%M:%SZ")
        }
    ]
}

response = requests.post(event_url, headers=headers, json=event_data)
response.raise_for_status()  # Check for HTTP errors
event_response = response.json()

# Save the event ID for next steps
event_id = event_response["event_id"]
event_url_link = event_response["event_url"]

print(f"Event created successfully")
print(f"Event ID: {event_id}")
print(f"Status: {event_response['status']}")
print(f"Registration URL: {event_url_link}")
```

### JavaScript (Node.js)

```javascript
// Create event
async function createEvent(token, hubId) {
    const eventUrl = "https://api.zoom.us/v2/zoom_events/events";

    // Event date: December 15, 2025, 2:00 PM EST (7:00 PM UTC)
    const startTime = new Date("2025-12-15T19:00:00Z");
    const endTime = new Date("2025-12-15T20:30:00Z");

    const eventData = {
        hub_id: hubId, // From Step 2
        name: "Q4 Town Hall: Looking Ahead to 2026",
        timezone: "America/New_York",
        event_type: "SIMPLE_EVENT",
        access_level: "PRIVATE_UNRESTRICTED",
        meeting_type: "WEBINAR",
        attendance_type: "VIRTUAL",
        calendar: [
            {
                start_time: startTime.toISOString(),
                end_time: endTime.toISOString(),
            },
        ],
    };

    try {
        const response = await axios.post(eventUrl, eventData, {
            headers: {
                Authorization: `Bearer ${token}`,
                "Content-Type": "application/json",
            },
        });

        const eventId = response.data.event_id;
        const eventUrlLink = response.data.event_url;

        console.log("Event created successfully");
        console.log(`Event ID: ${eventId}`);
        console.log(`Status: ${response.data.status}`);
        console.log(`Registration URL: ${eventUrlLink}`);

        return { eventId, eventUrlLink };
    } catch (error) {
        console.error(
            "Failed to create event:",
            error.response?.data || error.message,
        );
        throw error;
    }
}

const { eventId, eventUrlLink } = await createEvent(token, hubId);
```

### Example response

```json
{
    "event_id": "kNqLPC6hSFiZ9NpgjA549w",
    "name": "Q4 Town Hall: Looking Ahead to 2026",
    "timezone": "America/New_York",
    "event_type": "SIMPLE_EVENT",
    "access_level": "PRIVATE_UNRESTRICTED",
    "meeting_type": "WEBINAR",
    "attendance_type": "VIRTUAL",
    "status": "draft",
    "hub_id": "dKj3xR9bQYq5mNp8wA7cZf",
    "event_url": "https://events.zoom.us/event/kNqLPC6hSFiZ9NpgjA549w"
}
```

### Results

-   Zoom creates a new event in draft status.
-   Returns an `event_id` (this is critical - save it).
-   Returns an `event_url` (the registration page link).
-   Event not visible to public yet (still in draft).

### Understanding the fields

| Field             | Value Used             | Why                                                |
| ----------------- | ---------------------- | -------------------------------------------------- |
| `hub_id`          | From Step 2            | Required container for the event                   |
| `event_type`      | `SIMPLE_EVENT`         | Single-session event (vs multi-session conference) |
| `access_level`    | `PRIVATE_UNRESTRICTED` | Anyone with link can register                      |
| `meeting_type`    | `WEBINAR`              | Broadcast mode (100K capacity vs 1K for MEETING)   |
| `attendance_type` | `VIRTUAL`              | Online only (vs IN-PERSON or HYBRID)               |

**Save the event_id** - It is required for later steps.

---

## Step 4: Verify the event

Before publishing, verify the event was created correctly and review its current configuration.

### What this step does

Retrieving the event details to confirm all settings are correct.

### cURL

```shell
# Verify event (replace YOUR_ACCESS_TOKEN and YOUR_EVENT_ID)
curl https://api.zoom.us/v2/zoom_events/events/YOUR_EVENT_ID \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

### Python

```python
import requests

# Verify event details
verify_url = f"https://api.zoom.us/v2/zoom_events/events/{event_id}"
headers = {
    "Authorization": f"Bearer {access_token}"
}

response = requests.get(verify_url, headers=headers)
response.raise_for_status()  # Check for HTTP errors
event_details = response.json()

# Check critical fields
print("Event Verification:")
print(f"  Name: {event_details['name']}")
print(f"  Status: {event_details['status']}")  # Should be draft
print(f"  Event ID: {event_details['event_id']}")
print(f"  Event URL: {event_details['event_url']}")
print(f"  Meeting Type: {event_details['meeting_type']}")
print(f"  Access Level: {event_details['access_level']}")

# Verification checks
assert event_details['status'] == 'draft', "Event should be in draft status"
assert event_details['event_id'] == event_id, "Event ID should match"
print("\n✓ Event verified successfully")
```

### JavaScript (Node.js)

```javascript
// Verify event details
async function verifyEvent(token, eventId) {
    const verifyUrl = `https://api.zoom.us/v2/zoom_events/events/${eventId}`;

    const response = await axios.get(verifyUrl, {
        headers: {
            Authorization: `Bearer ${token}`,
        },
    });

    const eventDetails = response.data;

    // Check critical fields
    console.log("Event Verification:");
    console.log(`  Name: ${eventDetails.name}`);
    console.log(`  Status: ${eventDetails.status}`); // Should be draft
    console.log(`  Event ID: ${eventDetails.event_id}`);
    console.log(`  Event URL: ${eventDetails.event_url}`);
    console.log(`  Meeting Type: ${eventDetails.meeting_type}`);
    console.log(`  Access Level: ${eventDetails.access_level}`);

    // Verification checks
    if (eventDetails.status == "draft") {
        throw new Error("Event should be in draft status");
    }
    if (eventDetails.event_id == eventId) {
        throw new Error("Event ID should match");
    }

    console.log("\n✓ Event verified successfully");
    return eventDetails;
}

await verifyEvent(token, eventId);
```

### Results

-   Zoom returns complete event details.
-   Verify the status is in `draft`.
-   All your configuration fields are preserved.
-   The event is not yet visible to the public.

### Verification checklist

-   Status is `draft`
-   Event ID matches what you saved
-   Event URL is present
-   Name is correct
-   Meeting type is `WEBINAR`

---

## Step 5: Publish the event

Now that you have verified everything looks correct, publish the event to make it live and accessible to attendees.

### What this step does

Changing the event status from draft to Published, making it visible and allowing registrations.

### Important

After publishing, certain fields become locked and cannot be changed:

-   `event_type` (cannot change SIMPLE_EVENT to CONFERENCE).
-   `meeting_type` (cannot change WEBINAR to MEETING).

### cURL

```shell
# Publish event (replace YOUR_ACCESS_TOKEN and YOUR_EVENT_ID)
curl -X POST https://api.zoom.us/v2/zoom_events/events/YOUR_EVENT_ID/event_actions \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "operation": "publish"
  }'
```

### Python

```python
import requests

# Publish event
publish_url = f"https://api.zoom.us/v2/zoom_events/events/{event_id}/event_actions"
headers = {
    "Authorization": f"Bearer {access_token}",
    "Content-Type": "application/json"
}
    publish_data = {
        "operation": "publish"
    }

response = requests.post(publish_url, headers=headers, json=publish_data)
response.raise_for_status()  # Check for HTTP errors
publish_response = response.json()

print("Event published successfully")
print(f"Status: {publish_response['status']}")
print(f"Event ID: {publish_response['event_id']}")
print(f"\nYour event is now live")
print(f"Share this registration link: {event_url_link}")
```

### JavaScript (Node.js)

```javascript
// Publish event
async function publishEvent(token, eventId) {
    const publishUrl = `https://api.zoom.us/v2/zoom_events/events/${eventId}/event_actions`;

    const publishData = {
        operation: "publish",
    };

    try {
        const response = await axios.post(publishUrl, publishData, {
            headers: {
                Authorization: `Bearer ${token}`,
                "Content-Type": "application/json",
            },
        });

        console.log("Event published successfully");
        console.log(`Status: ${response.data.status}`);
        console.log(`Event ID: ${response.data.event_id}`);
        console.log("\n Your event is now live");
        console.log(`Share this registration link: ${eventUrlLink}`);

        return response.data;
    } catch (error) {
        console.error(
            "Failed to publish event:",
            error.response?.data || error.message,
        );
        throw error;
    }
}

await publishEvent(token, eventId);
```

### Example response

```json
{
    "status": "published",
    "event_id": "kNqLPC6hSFiZ9NpgjA549w"
}
```

### Results

-   Event status changes from `draft` to `Published`.
-   Registration page becomes publicly accessible.
-   Attendees can now register via the `event_url`.
-   Event appears in your hub's event list.
-   Certain fields are now locked (event_type, meeting_type).

---

## Step 6: Verify publication and test registration

Confirm the event is truly live and the registration page is accessible.

### What this step does

Verifying the published status and testing that the registration page loads correctly.

### cURL

```shell
# Verify published status
curl https://api.zoom.us/v2/zoom_events/events/YOUR_EVENT_ID \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  | grep -E '"status"|"event_url"'
```

### Python

```python
import requests
import webbrowser

# Verify published status
verify_url = f"https://api.zoom.us/v2/zoom_events/events/{event_id}"
headers = {
    "Authorization": f"Bearer {access_token}"
}

response = requests.get(verify_url, headers=headers)
response.raise_for_status()  # Check for HTTP errors
final_event = response.json()

print("Final Verification:")
print(f"  Status: {final_event['status']}")  # Should be PUBLISHED
print(f"  Event URL: {final_event['event_url']}")

# Check status is published
if final_event['status'] == 'Published':
    print("\n✓ Event is Published and live")
    print(f"\nRegistration page: {final_event['event_url']}")

    # Optional: Open in browser
    # webbrowser.open(final_event['event_url'])
else:
    print(f"\n✗ Warning: Event status is {final_event['status']}, expected Published")
```

### JavaScript (Node.js)

```javascript
// Verify published status
async function verifyPublished(token, eventId, eventUrlLink) {
    const verifyUrl = `https://api.zoom.us/v2/zoom_events/events/${eventId}`;

    const response = await axios.get(verifyUrl, {
        headers: {
            Authorization: `Bearer ${token}`,
        },
    });

    const finalEvent = response.data;

    console.log("Final Verification:");
    console.log(`  Status: ${finalEvent.status}`); // Should be Published
    console.log(`  Event URL: ${finalEvent.event_url}`);

    // Check status is published
    if (finalEvent.status === "Published") {
        console.log("\n✓ Event is Published and live");
        console.log(`\nRegistration page: ${finalEvent.event_url}`);
    } else {
        console.log(
            `\n✗ Warning: Event status is ${finalEvent.status}, expected Published`,
        );
    }

    return finalEvent;
}

await verifyPublished(token, eventId, eventUrlLink);
```

### Manual test

1. Copy the `event_url` from the response.
2. Open it in your web browser.
3. Verify the registration page with your event name.

### Results

-   API confirms status is `Published`.
-   Event URL is accessible without authentication.
-   Registration page displays event details.
-   Attendees can start registering.

---

## What you built

Congratulations! You have successfully

-   **Authenticated** with Zoom's OAuth API.
-   **Retrieved** your hub ID.
-   **Created** a new event (Q4 Town Hall).
-   **Verified** the event configuration.
-   **Published** the event to make it live.
-   **Confirmed** the registration page is accessible.

### Your event details

-   **Type**: Single-session virtual event (SIMPLE_EVENT).
-   **Format**: Webinar (broadcast mode, up to 100K attendees).
-   **Access**: Private but unrestricted (anyone with link can register).
-   **Status**: Published and ready for registrations.

### The registration flow

1. Attendees visit the `event_url` you created.
2. They fill out registration form.
3. They receive confirmation email with join link.
4. On event day, they click join link to enter the webinar.

---

## Complete working example

Here is the entire workflow in one script for each language:

### Python (complete)

```python
import requests
import sys

def create_and_publish_event():
    """Complete workflow: authenticate, create, and publish event"""

    # Configuration
    client_id = "YOUR_CLIENT_ID"
    client_secret = "YOUR_CLIENT_SECRET"

    print("Step 1: Getting access token...")
    # Authenticate
    token_response = requests.post(
        "https://zoom.us/oauth/token",
        data={"grant_type": "client_credentials"},
        auth=(client_id, client_secret)
    )
    access_token = token_response.json()["access_token"]
    print(f" Token obtained: {access_token[:20]}...")

    # Setup headers
    headers = {
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json"
    }

    print("\nStep 2: Getting hub ID...")
    # Get hub ID
    hub_response = requests.get(
        "https://api.zoom.us/v2/zoom_events/hubs",
        headers=headers,
        params={"role_type": "host"}
    )
    hub_id = hub_response.json()["hubs"][0]["hub_id"]
    print(f" Using hub ID: {hub_id}")

    print("\nStep 3: Creating event...")
    # Create event
    # Event date: December 15, 2025, 2:00 PM EST (7:00 PM UTC)
    from datetime import datetime, timezone
    start_time = datetime(2025, 12, 15, 19, 0, 0, tzinfo=timezone.utc)
    end_time = datetime(2025, 12, 15, 20, 30, 0, tzinfo=timezone.utc)

    event_data = {
        "hub_id": hub_id,
        "name": "Q4 Town Hall: Looking Ahead to 2026",
        "timezone": "America/New_York",
        "event_type": "SIMPLE_EVENT",
        "access_level": "PRIVATE_UNRESTRICTED",
        "meeting_type": "WEBINAR",
        "attendance_type": "VIRTUAL",
        "calendar": [
            {
                "start_time": start_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
                "end_time": end_time.strftime("%Y-%m-%dT%H:%M:%SZ")
            }
        ]
    }

    event_response = requests.post(
        "https://api.zoom.us/v2/zoom_events/events",
        headers=headers,
        json=event_data
    )
    event_response.raise_for_status()  # Check for HTTP errors
    event_result = event_response.json()
    event_id = event_result["event_id"]
    event_url = event_result["event_url"]
    print(f" Event created: {event_id}")
    print(f"  Status: {event_result['status']}")

    print("\nStep 4: Verifying event...")
    # Verify event
    verify_response = requests.get(
        f"https://api.zoom.us/v2/zoom_events/events/{event_id}",
        headers=headers
    )
    verify_data = verify_response.json()
    assert verify_data['status'] == 'draft', "Event should be in draft"
    print(" Event verified")

    print("\nStep 5: Publishing event...")
    # Publish event
    publish_response = requests.post(
        f"https://api.zoom.us/v2/zoom_events/events/{event_id}/event_actions",
        headers=headers,
        json={"operation": "publish"}
    )
    publish_response.raise_for_status()  # Check for HTTP errors
    publish_data = publish_response.json()
    print(f" Event published: {publish_data['status']}")

    print("\nStep 6: Final verification...")
    # Final verification
    final_response = requests.get(
        f"https://api.zoom.us/v2/zoom_events/events/{event_id}",
        headers=headers
    )
    final_data = final_response.json()

    if final_data['status'] == 'Published':
        print(" Event is Published and live")
        print(f"\n{'='*60}")
        print("SUCCESS Your event is ready")
        print(f"{'='*60}")
        print(f"Event Name: {final_data['name']}")
        print(f"Event ID: {event_id}")
        print(f"Registration URL: {event_url}")
        print(f"\nShare this link with attendees to start collecting registrations")
        return event_id, event_url
    else:
        print(f"✗ Unexpected status: {final_data['status']}")
        sys.exit(1)

if __name__ == "__main__":
    create_and_publish_event()
```

### JavaScript (complete)

```javascript
const axios = require("axios");

// Configuration
const CLIENT_ID = "YOUR_CLIENT_ID";
const CLIENT_SECRET = "YOUR_CLIENT_SECRET";

async function createAndPublishEvent() {
    try {
        console.log("Step 1: Getting access token...");
        // Authenticate
        const tokenResponse = await axios.post(
            "https://zoom.us/oauth/token",
            "grant_type=client_credentials",
            {
                auth: { username: CLIENT_ID, password: CLIENT_SECRET },
                headers: {
                    "Content-Type": "application/x-www-form-urlencoded",
                },
            },
        );
        const accessToken = tokenResponse.data.access_token;
        console.log(` Token obtained: ${accessToken.substring(0, 20)}...`);

        // Setup headers
        const headers = {
            Authorization: `Bearer ${accessToken}`,
            "Content-Type": "application/json",
        };

        console.log("\nStep 2: Getting hub ID...");
        // Get hub ID
        const hubResponse = await axios.get(
            "https://api.zoom.us/v2/zoom_events/hubs",
            { headers, params: { role_type: "host" } },
        );
        const hubId = hubResponse.data.hubs[0].hub_id;
        console.log(` Using hub ID: ${hubId}`);

        console.log("\nStep 3: Creating event...");
        // Create event
        // Event date: December 15, 2025, 2:00 PM EST (7:00 PM UTC)
        const startTime = new Date("2025-12-15T19:00:00Z");
        const endTime = new Date("2025-12-15T20:30:00Z");

        const eventData = {
            hub_id: hubId,
            name: "Q4 Town Hall: Looking Ahead to 2026",
            timezone: "America/New_York",
            event_type: "SIMPLE_EVENT",
            access_level: "PRIVATE_UNRESTRICTED",
            meeting_type: "WEBINAR",
            attendance_type: "VIRTUAL",
            calendar: [
                {
                    start_time: startTime.toISOString(),
                    end_time: endTime.toISOString(),
                },
            ],
        };

        const eventResponse = await axios.post(
            "https://api.zoom.us/v2/zoom_events/events",
            eventData,
            { headers },
        );
        const eventId = eventResponse.data.event_id;
        const eventUrl = eventResponse.data.event_url;
        console.log(` Event created: ${eventId}`);
        console.log(`  Status: ${eventResponse.data.status}`);

        console.log("\nStep 4: Verifying event...");
        // Verify event
        const verifyResponse = await axios.get(
            `https://api.zoom.us/v2/zoom_events/events/${eventId}`,
            { headers },
        );
        if (verifyResponse.data.status == "draft") {
            throw new Error("Event should be in draft status");
        }
        console.log(" Event verified");

        console.log("\nStep 5: Publishing event...");
        // Publish event
        const publishResponse = await axios.post(
            `https://api.zoom.us/v2/zoom_events/events/${eventId}/event_actions`,
            { operation: "publish" },
            { headers },
        );
        console.log(` Event published: ${publishResponse.data.status}`);

        console.log("\nStep 6: Final verification...");
        // Final verification
        const finalResponse = await axios.get(
            `https://api.zoom.us/v2/zoom_events/events/${eventId}`,
            { headers },
        );

        if (finalResponse.data.status === "Published") {
            console.log(" Event is Published and live");
            console.log("\n" + "=".repeat(60));
            console.log(" SUCCESS Your event is ready");
            console.log("=".repeat(60));
            console.log(`Event Name: ${finalResponse.data.name}`);
            console.log(`Event ID: ${eventId}`);
            console.log(`Registration URL: ${eventUrl}`);
            console.log(
                "\nShare this link with attendees to start collecting registrations",
            );
            return { eventId, eventUrl };
        } else {
            throw new Error(`Unexpected status: ${finalResponse.data.status}`);
        }
    } catch (error) {
        console.error("Error:", error.response?.data || error.message);
        process.exit(1);
    }
}

// Run the workflow
createAndPublishEvent();
```

---

## Common issues and solutions

### Issue: "No permission to create event" (error 26201)

#### Cause

Your OAuth token doesn't have the required scope or you are using the wrong hub_id.

#### Solution

-   Verify your OAuth app has `zoom_events:write:event` scope.
-   Confirm the hub_id you are using is correct (from Step 2.
-   Ensure the account owner granted permission to your OAuth app.

### Issue: "Invalid event ID" (error 1001)

#### Cause

Typo in the event_id or using an event_id from a different account.

#### Solution

-   Copy the event_id directly from Step 3 response.
-   Do not manually type the event_id.
-   Ensure you are using the same access token throughout.

### Issue: "Event not found" (error 2002)

#### Cause

Event was deleted or never existed.

#### Solution

-   Verify the event_id is correct.
-   Check if event was accidentally deleted.
-   Create a new event starting from Step 3.

### Issue: Token expired during workflow

#### Cause

OAuth tokens expire after ~1 hour.

#### Solution

-   Complete the workflow within one session.
-   If token expires, re-run Step 1 to get a new token.
-   Consider implementing token refresh logic in production code.

---

## Key concepts explained

### Event lifecycle states

```plaintext
Draft → Published → [LIVE] → Completed
  ↓
Cancelled (from Published state only)
```

-   **DRAFT**: Event is being configured, not visible to public.
-   **PUBLISHED**: Event is live, registration open, visible on hub.
-   **CANCELLED**: Event was published but then cancelled .(requires cancellation message if registrants exist).

### Event type comparison

| Event Type     | Use Case            | Session Count                | Example            |
| -------------- | ------------------- | ---------------------------- | ------------------ |
| `SIMPLE_EVENT` | Single session      | 1                            | Town hall, webinar |
| `RECURRING`    | Repeating series    | Multiple (same content)      | Weekly training    |
| `CONFERENCE`   | Multi-session event | Multiple (different content) | Annual summit      |

### Access levels

| Access Level           | Who Can Register             | Use Case                     |
| ---------------------- | ---------------------------- | ---------------------------- |
| `PRIVATE_UNRESTRICTED` | Anyone with link             | Internal events, semi-public |
| `PRIVATE_RESTRICTED`   | Approved domains/emails only | Corporate events             |

### Meeting type capacity

| Meeting Type | Max Capacity | Mode                              | Best For                        |
| ------------ | ------------ | --------------------------------- | ------------------------------- |
| `WEBINAR`    | 100,000      | Broadcast (panelists + attendees) | Large audiences, presentations  |
| `MEETING`    | 1,000        | Collaborative (all participants)  | Workshops, interactive sessions |
