Exchange credentials for access token
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—30 minutes
Prerequisites
- Pro+ Zoom account with Webinars Plus or Events license
- OAuth credentials (Client ID and Secret) - see Zoom's 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
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
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)
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
Authorizationheader 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
# 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
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)
// 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
{
"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_idwill 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
# 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
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)
// 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
{
"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
# 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
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)
// 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
# 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
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)
// 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
{
"status": "published",
"event_id": "kNqLPC6hSFiZ9NpgjA549w"
}
Results
- Event status changes from
drafttoPublished. - 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
# 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
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)
// 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
- Copy the
event_urlfrom the response. - Open it in your web browser.
- 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
- Attendees visit the
event_urlyou created. - They fill out registration form.
- They receive confirmation email with join link.
- 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)
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)
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:eventscope. - 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
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 |