# First create the recurring event

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

## What you will build

A recurring event series with automated scheduling, unified registration, and per-occurrence management.

## Use cases covered

-   Weekly team standup meetings, occurring every Monday at 10 a.m. for 12 weeks.
-   Monthly webinar series occurring on the first Tuesday of each month for six months.
-   Custom recurring patterns with flexible registration options.

## Time required

15 minutes

---

## What you will learn

By the end of this tutorial, you will know how to:

1. Create recurring event series with daily, weekly, or monthly patterns.
2. Configure different registration options (series-level vs per-occurrence).
3. List and manage individual occurrences.
4. Register attendees for specific sessions or entire series.

---

## Prerequisites

Before starting, ensure you have:

-   **Account:** Pro or higher plan
-   **License:** Zoom Webinars Plus or Events
-   **API access:** OAuth app with `zoom_events:write:event` and `zoom_events:read:list_sessions` scopes
-   **Hub access:** Host role in at least one hub
-   **Access token:** Valid OAuth token for authentication

---

## Scenario 1: Weekly team standup series

Create a recurring standup meeting that runs every Monday at 10am Eastern Time for 12 weeks (Q1 2025).

### Step 1: Get your Hub ID

First, retrieve the hub where you will create the event.

### cURL

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

### Python

```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()
hub_id = hubs['hubs'][0]['hub_id']
print(f"Hub ID: {hub_id}")
```

### JavaScript (Node.js)

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

async function getHubId() {
    try {
        const response = await axios.get(
            "https://api.zoom.us/v2/zoom_events/hubs",
            {
                headers: { Authorization: "Bearer YOUR_ACCESS_TOKEN" },
                params: { role_type: "host" },
            },
        );

        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;
    }
}
```

### Response

```json
{
    "total_records": 1,
    "hubs": [
        {
            "hub_id": "4uzfv3JwTeyR5QpC3PXwMg",
            "name": "Engineering Team Hub"
        }
    ]
}
```

Save the `hub_id` value—you'll need it in the next step.

---

### Step 2: Create the weekly recurring event

Now create a recurring event with a weekly pattern. The event starts January 6, 2025 (first Monday) and repeats every Monday for 12 weeks.

**Understanding the recurrence pattern:**

-   `type: 2` = Weekly recurrence
-   `repeat_interval: 1` = Every 1 week (not every 2 weeks)
-   `monthly_week_day: 1` = Monday (0=Sunday, 1=Monday, 2=Tuesday, etc.)
-   `end_times: 12` = Generate 12 occurrences
-   `duration: 30` = Each session lasts 30 minutes

### cURL

```shell
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": "4uzfv3JwTeyR5QpC3PXwMg",
    "name": "Engineering Team Weekly Standup",
    "timezone": "America/New_York",
    "event_type": "RECURRING",
    "access_level": "PRIVATE_UNRESTRICTED",
    "meeting_type": "MEETING",
    "attendance_type": "VIRTUAL",
    "calendar": [
        {
          "start_time": "2025-01-06T15:00:00Z"
        }
     ],
    "recurrence": {
      "type": 2,
      "repeat_interval": 1,
      "monthly_week_day": 1,
      "end_times": 12,
      "duration": 30
    }
  }'
```

### Python

```python
import requests
from datetime import datetime

url = "https://api.zoom.us/v2/zoom_events/events"
headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer YOUR_ACCESS_TOKEN"
}

payload = {
    "hub_id": hub_id,
    "name": "Engineering Team Weekly Standup",
    "timezone": "America/New_York",
    "event_type": "RECURRING",
    "access_level": "PRIVATE_UNRESTRICTED",
    "meeting_type": "MEETING",
    "attendance_type": "VIRTUAL",
     "calendar": [
        {
         "start_time": "2025-01-06T15:00:00Z"  # 10am ET = 3pm UTC
        }
     ],
    "recurrence": {
        "type": 2,                    # Weekly
        "repeat_interval": 1,         # Every week
        "monthly_week_day": 1,        # Monday
        "end_times": 12,              # 12 occurrences
        "duration": 30                # 30 minutes each
    }
}

response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()  # Check for HTTP errors
event_data = response.json()
event_id = event_data['event_id']
print(f"Created recurring event: {event_id}")
print(f"Event URL: {event_data['event_url']}")
```

### JavaScript (Node.js)

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

async function createWeeklyStandup(hubId) {
    try {
        const response = await axios.post(
            "https://api.zoom.us/v2/zoom_events/events",
            {
                hub_id: hubId,
                name: "Engineering Team Weekly Standup",
                timezone: "America/New_York",
                event_type: "RECURRING",
                access_level: "PRIVATE_UNRESTRICTED",
                meeting_type: "MEETING",
                attendance_type: "VIRTUAL",
                calendar: [
                    {
                        start_time: "2025-01-06T15:00:00Z",
                    },
                ],
                recurrence: {
                    type: 2, // Weekly
                    repeat_interval: 1, // Every week
                    monthly_week_day: 1, // Monday
                    end_times: 12, // 12 occurrences
                    duration: 30, // 30 minutes each
                },
            },
            {
                headers: {
                    "Content-Type": "application/json",
                    Authorization: "Bearer YOUR_ACCESS_TOKEN",
                },
            },
        );

        console.log(`Event ID: ${response.data.event_id}`);
        console.log(`Event URL: ${response.data.event_url}`);
        return response.data.event_id;
    } catch (error) {
        console.error(
            "Failed to create recurring event:",
            error.response?.data || error.message,
        );
        throw error;
    }
}
```

### Response

```json
{
    "event_id": "kNqLPC6hSFiZ9NpgjA549w",
    "name": "Engineering Team Weekly Standup",
    "event_type": "RECURRING",
    "status": "draft",
    "event_url": "https://events.zoom.us/e/kNqLPC6hSFiZ9NpgjA549w",
    "calendar": [
        {
            "start_time": "2025-01-06T15:00:00Z"
        }
    ],
    "recurrence": {
        "type": 2,
        "repeat_interval": 1,
        "monthly_week_day": 1,
        "end_times": 12,
        "duration": 30
    }
}
```

The system automatically generates 12 Monday occurrences starting January 6, 2025.

---

### Step 3: Create a Ticket Type for registration

Before publishing, create a ticket type (required even for free events).

### cURL

```shell
curl -X POST 'https://api.zoom.us/v2/zoom_events/events/kNqLPC6hSFiZ9NpgjA549w/ticket_types' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -d '{
    "name": "Team Member Pass",
    "free": true,
    "quantity": 50
  }'
```

### Python

```python
ticket_url = f"https://api.zoom.us/v2/zoom_events/events/{event_id}/ticket_types"
ticket_payload = {
    "name": "Team Member Pass",
    "free": True,
    "quantity": 50
}

ticket_response = requests.post(ticket_url, headers=headers, json=ticket_payload)
ticket_response.raise_for_status()  # Check for HTTP errors
ticket_type_id = ticket_response.json()['ticket_type_id']
print(f"Ticket Type ID: {ticket_type_id}")
```

### JavaScript

```javascript
async function createTicketType(eventId) {
    try {
        const response = await axios.post(
            `https://api.zoom.us/v2/zoom_events/events/${eventId}/ticket_types`,
            {
                name: "Team Member Pass",
                free: true,
                quantity: 50,
            },
            {
                headers: {
                    "Content-Type": "application/json",
                    Authorization: "Bearer YOUR_ACCESS_TOKEN",
                },
            },
        );
        return response.data.ticket_type_id;
    } catch (error) {
        console.error(
            "Failed to create ticket type:",
            error.response?.data || error.message,
        );
        throw error;
    }
}
```

---

### Step 4: Configure registration access link

Create an access link with registration option. For this standup series, we'll use **"all_sessions"** registration so team members register once for the entire quarter.

### cURL

```shell
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": "Team Registration Link",
    "type": "registration",
    "is_default": true,
    "ticket_type_id": "TICKET_TYPE_ID",
    "recurring_registration_option": "all_sessions",
    "authentication_method": "bypass_auth"
  }'
```

### Python

```python
access_link_url = f"https://api.zoom.us/v2/zoom_events/events/{event_id}/access_links"
access_link_payload = {
    "name": "Team Registration Link",
    "type": "registration",
    "is_default": True,
    "ticket_type_id": ticket_type_id,
    "recurring_registration_option": "all_sessions",
    "authentication_method": "bypass_auth"
}

access_response = requests.post(access_link_url, headers=headers, json=access_link_payload)
access_response.raise_for_status()  # Check for HTTP errors
registration_url = access_response.json()['registration_url']
print(f"Registration URL: {registration_url}")
```

### JavaScript

```javascript
async function createAccessLink(eventId, ticketTypeId) {
    try {
        const response = await axios.post(
            `https://api.zoom.us/v2/zoom_events/events/${eventId}/access_links`,
            {
                name: "Team Registration Link",
                type: "registration",
                is_default: true,
                ticket_type_id: ticketTypeId,
                recurring_registration_option: "all_sessions",
                authentication_method: "bypass_auth",
            },
            {
                headers: {
                    "Content-Type": "application/json",
                    Authorization: "Bearer YOUR_ACCESS_TOKEN",
                },
            },
        );
        console.log(`Registration URL: ${response.data.registration_url}`);
        return response.data;
    } catch (error) {
        console.error(
            "Failed to create access link:",
            error.response?.data || error.message,
        );
        throw error;
    }
}
```

**What "all_sessions" means**

When an attendee registers once, they receive a single ticket granting access to all 12 Monday standup sessions.

---

### Step 5: Publish the event

Make the event live and start accepting registrations.

### cURL

```shell
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

```python
publish_url = f"https://api.zoom.us/v2/zoom_events/events/{event_id}/event_actions"
publish_payload = {"operation": "publish"}

publish_response = requests.post(publish_url, headers=headers, json=publish_payload)
publish_response.raise_for_status()  # Check for HTTP errors
if publish_response.status_code == 200:
    print("Event published successfully")
```

## JavaScript

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

---

### Step 6: Verify all occurrences were created

List all generated session occurrences to confirm the 12-week schedule.

### cURL

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

### Python

```python
sessions_url = f"https://api.zoom.us/v2/zoom_events/events/{event_id}/sessions"
sessions_response = requests.get(sessions_url, headers=headers)
sessions_response.raise_for_status()  # Check for HTTP errors
sessions_data = sessions_response.json()

print(f"Total occurrences: {sessions_data['total_records']}")
for i, session in enumerate(sessions_data['sessions'], 1):
    print(f"Week {i}: {session['start_time']} (Session ID: {session['session_id']})")
```

### JavaScript

```javascript
async function listSessions(eventId) {
    try {
        const response = await axios.get(
            `https://api.zoom.us/v2/zoom_events/events/${eventId}/sessions`,
            { headers: { Authorization: "Bearer YOUR_ACCESS_TOKEN" } },
        );

        console.log(`Total occurrences: ${response.data.total_records}`);
        response.data.sessions.forEach((session, index) => {
            console.log(
                `Week ${index + 1}: ${session.start_time} (ID: ${session.session_id})`,
            );
        });
        return response.data.sessions;
    } catch (error) {
        console.error(
            "Failed to list sessions:",
            error.response?.data || error.message,
        );
        throw error;
    }
}
```

### Response

```json
{
    "total_records": 12,
    "sessions": [
        {
            "session_id": "aVSyJswkTGC4o1gqVogTaA",
            "start_time": "2025-01-06T15:00:00Z",
            "end_time": "2025-01-06T15:30:00Z"
        },
        {
            "session_id": "SPbxN1VwSrygHG0N6T2YtQ",
            "start_time": "2025-01-13T15:00:00Z",
            "end_time": "2025-01-13T15:30:00Z"
        }
        // ...
    ]
}
```

Now, all 12 Monday occurrences are scheduled.

---

## Scenario 2: Monthly webinar series

Create a monthly webinar that runs on the **first Tuesday** of each month at 2pm Pacific Time for 6 months.

### Create monthly recurring webinar

**Understanding monthly patterns:**

-   `type: 3` = Monthly recurrence
-   `repeat_interval: 1` = Every 1 month
-   `monthly_week: 1` = First week of the month (1=first, 2=second, 3=third, 4=fourth)
-   `monthly_week_day: 2` = Tuesday (0=Sunday, 1=Monday, 2=Tuesday, etc.)
-   `end_times: 6` = Generate 6 monthly occurrences
-   `duration: 90` = Each webinar lasts 90 minutes

### cURL

```shell
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": "4uzfv3JwTeyR5QpC3PXwMg",
    "name": "Product Updates Webinar Series",
    "timezone": "America/Los_Angeles",
    "event_type": "RECURRING",
    "access_level": "PRIVATE_UNRESTRICTED",
    "meeting_type": "WEBINAR",
    "attendance_type": "VIRTUAL",
    "calendar": [
      {
       "start_time": "2025-02-04T22:00:00Z"
      }
    ],
    "recurrence": {
      "type": 3,
      "repeat_interval": 1,
      "monthly_week": 1,
      "monthly_week_day": 2,
      "end_times": 6,
      "duration": 90
    }
  }'
```

### Python

```python
monthly_payload = {
    "hub_id": hub_id,
    "name": "Product Updates Webinar Series",
    "timezone": "America/Los_Angeles",
    "event_type": "RECURRING",
    "access_level": "PRIVATE_UNRESTRICTED",
    "meeting_type": "WEBINAR",
    "attendance_type": "VIRTUAL",
    "calendar": [
      {
      "start_time": "2025-02-04T22:00:00Z"  # 2pm PT = 10pm UTC
      }
    ],
    "recurrence": {
        "type": 3,                    # Monthly
        "repeat_interval": 1,         # Every month
        "monthly_week": 1,            # First week
        "monthly_week_day": 2,        # Tuesday
        "end_times": 6,               # 6 months
        "duration": 90                # 90 minutes
    }
}

response = requests.post(
    "https://api.zoom.us/v2/zoom_events/events",
    headers=headers,
    json=monthly_payload
)
response.raise_for_status()  # Check for HTTP errors
webinar_event_id = response.json()['event_id']
print(f"Monthly webinar series created: {webinar_event_id}")
```

## JavaScript

```javascript
async function createMonthlyWebinar(hubId) {
    try {
        const response = await axios.post(
            "https://api.zoom.us/v2/zoom_events/events",
            {
                hub_id: hubId,
                name: "Product Updates Webinar Series",
                timezone: "America/Los_Angeles",
                event_type: "RECURRING",
                access_level: "PRIVATE_UNRESTRICTED",
                meeting_type: "WEBINAR",
                attendance_type: "VIRTUAL",
                calendar: [
                    {
                        start_time: "2025-02-04T22:00:00Z",
                    },
                ],
                recurrence: {
                    type: 3, // Monthly
                    repeat_interval: 1, // Every month
                    monthly_week: 1, // First week
                    monthly_week_day: 2, // Tuesday
                    end_times: 6, // 6 months
                    duration: 90, // 90 minutes
                },
            },
            {
                headers: {
                    "Content-Type": "application/json",
                    Authorization: "Bearer YOUR_ACCESS_TOKEN",
                },
            },
        );
        return response.data.event_id;
    } catch (error) {
        console.error(
            "Failed to create monthly webinar:",
            error.response?.data || error.message,
        );
        throw error;
    }
}
```

This creates webinars on:

-   Feb 4, 2025 (1st Tuesday)
-   Mar 4, 2025 (1st Tuesday)
-   Apr 1, 2025 (1st Tuesday)
-   May 6, 2025 (1st Tuesday)
-   Jun 3, 2025 (1st Tuesday)
-   Jul 1, 2025 (1st Tuesday)

---

## Scenario 3: Per-session registration

Sometimes attendees shouldn't register for all sessions—they should pick which specific occurrences to attend. This section shows you how to configure **single_session** registration for a training series.

### Create training series with single-session registration

### cURL

```shell
# First create the recurring event
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": "4uzfv3JwTeyR5QpC3PXwMg",
    "name": "Git & GitHub Training Series",
    "timezone": "America/New_York",
    "event_type": "RECURRING",
    "access_level": "PRIVATE_UNRESTRICTED",
    "meeting_type": "WEBINAR",
    "attendance_type": "VIRTUAL",
    "calendar": [
       {
       "start_time": "2025-01-08T18:00:00Z"
       }
    ],
    "recurrence": {
      "type": 2,
      "repeat_interval": 1,
      "monthly_week_day": 3,
      "end_times": 4,
      "duration": 60
    }
  }'

# Create ticket type
curl -X POST 'https://api.zoom.us/v2/zoom_events/events/{eventId}/ticket_types' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -d '{
    "name": "Per-Session Ticket",
    "free": true,
    "quantity": 100
  }'

# Create access link with single_session registration
curl -X POST 'https://api.zoom.us/v2/zoom_events/events/{eventId}/access_links' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -d '{
    "name": "Training Registration",
    "type": "registration",
    "is_default": true,
    "ticket_type_id": "{ticket_type_id}",
    "recurring_registration_option": "single_session",
    "authentication_method": "bypass_auth"
  }'
```

### Python

```python
# Create event
training_payload = {
    "hub_id": hub_id,
    "name": "Git & GitHub Training Series",
    "timezone": "America/New_York",
    "event_type": "RECURRING",
    "access_level": "PRIVATE_UNRESTRICTED",
    "meeting_type": "WEBINAR",
    "attendance_type": "VIRTUAL",
    "calendar": [
       {
        "start_time": "2025-01-08T18:00:00Z"  # Wednesdays at 1pm ET
       }
    ],
    "recurrence": {
        "type": 2,
        "repeat_interval": 1,
        "monthly_week_day": 3,  # Wednesday
        "end_times": 4,
        "duration": 60
    }
}

event_response = requests.post(
    "https://api.zoom.us/v2/zoom_events/events",
    headers=headers,
    json=training_payload
)
event_response.raise_for_status()  # Check for HTTP errors
training_event_id = event_response.json()['event_id']

# Create ticket type
ticket_response = requests.post(
    f"https://api.zoom.us/v2/zoom_events/events/{training_event_id}/ticket_types",
    headers=headers,
    json={"name": "Per-Session Ticket", "type": "FREE", "inventory": 100}
)
ticket_response.raise_for_status()  # Check for HTTP errors
ticket_type_id = ticket_response.json()['ticket_type_id']

# Create access link with single_session option
access_response = requests.post(
    f"https://api.zoom.us/v2/zoom_events/events/{training_event_id}/access_links",
    headers=headers,
    json={
        "name": "Training Registration",
        "type": "registration",
        "is_default": True,
        "ticket_type_id": ticket_type_id,
        "recurring_registration_option": "single_session",
        "authentication_method": "bypass_auth"
    }
)
access_response.raise_for_status()  # Check for HTTP errors

print(f"Training series created with per-session registration")
```

### JavaScript

```javascript
async function createTrainingWithSingleSession(hubId) {
    try {
        // Create event
        const eventResponse = await axios.post(
            "https://api.zoom.us/v2/zoom_events/events",
            {
                hub_id: hubId,
                name: "Git & GitHub Training Series",
                timezone: "America/New_York",
                event_type: "RECURRING",
                access_level: "PRIVATE_UNRESTRICTED",
                meeting_type: "WEBINAR",
                attendance_type: "VIRTUAL",
                calendar: [
                    {
                        start_time: "2025-01-08T18:00:00Z",
                    },
                ],
                recurrence: {
                    type: 2,
                    repeat_interval: 1,
                    monthly_week_day: 3, // Wednesday
                    end_times: 4,
                    duration: 60,
                },
            },
            {
                headers: {
                    "Content-Type": "application/json",
                    Authorization: "Bearer YOUR_ACCESS_TOKEN",
                },
            },
        );

        const eventId = eventResponse.data.event_id;

        // Create ticket type
        const ticketResponse = await axios.post(
            `https://api.zoom.us/v2/zoom_events/events/${eventId}/ticket_types`,
            { name: "Per-Session Ticket", free: true, quantity: 100 },
            {
                headers: {
                    "Content-Type": "application/json",
                    Authorization: "Bearer YOUR_ACCESS_TOKEN",
                },
            },
        );

        const ticketTypeId = ticketResponse.data.ticket_type_id;

        // Create access link with single_session
        await axios.post(
            `https://api.zoom.us/v2/zoom_events/events/${eventId}/access_links`,
            {
                name: "Training Registration",
                type: "registration",
                is_default: true,
                ticket_type_id: ticketTypeId,
                recurring_registration_option: "single_session",
                authentication_method: "bypass_auth",
            },
            {
                headers: {
                    "Content-Type": "application/json",
                    Authorization: "Bearer YOUR_ACCESS_TOKEN",
                },
            },
        );

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

---

## Managing attendee registrations

### Register attendee for all sessions

When using `recurring_registration_option: "all_sessions"`, simply omit `session_ids` in the ticket creation request.

### cURL

```shell
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": "jane.doe@company.com",
        "first_name": "Jane",
        "last_name": "Doe",
        "ticket_type_id": "TICKET_TYPE_ID",
        "send_notification": true,
        "fast_join": false
      }
    ]
  }'
```

### Python

```python
def register_for_all_sessions(event_id, ticket_type_id, attendee_email, first_name, last_name):
    """Register attendee for entire series"""
    tickets_url = f"https://api.zoom.us/v2/zoom_events/events/{event_id}/tickets"
    payload = {
        "tickets": [{
            "email": attendee_email,
            "first_name": first_name,
            "last_name": last_name,
            "ticket_type_id": ticket_type_id,
            "send_notification": True,
            "fast_join": False
        }]
    }

    response = requests.post(tickets_url, headers=headers, json=payload)
    response.raise_for_status()  # Check for HTTP errors
    ticket_data = response.json()
    print(f"Registered {attendee_email} for all sessions")
    return ticket_data['tickets'][0]['ticket_id']

# Example usage
ticket_id = register_for_all_sessions(
    event_id,
    ticket_type_id,
    "jane.doe@company.com",
    "Jane",
    "Doe"
)
```

### JavaScript

```javascript
async function registerForAllSessions(
    eventId,
    ticketTypeId,
    email,
    firstName,
    lastName,
) {
    try {
        const response = await axios.post(
            `https://api.zoom.us/v2/zoom_events/events/${eventId}/tickets`,
            {
                tickets: [
                    {
                        email: email,
                        first_name: firstName,
                        last_name: lastName,
                        ticket_type_id: ticketTypeId,
                        send_notification: true,
                        fast_join: false,
                    },
                ],
            },
            {
                headers: {
                    "Content-Type": "application/json",
                    Authorization: "Bearer YOUR_ACCESS_TOKEN",
                },
            },
        );

        console.log(`Registered ${email} for all sessions`);
        return response.data.tickets[0].ticket_id;
    } catch (error) {
        console.error(
            "Failed to register attendee:",
            error.response?.data || error.message,
        );
        throw error;
    }
}
```

---

### Register attendee for specific sessions

When using `recurring_registration_option: "single_session"` or `"multiple_sessions"`, include the specific `session_ids` array.

### cURL

```shell
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": "john.smith@company.com",
        "first_name": "John",
        "last_name": "Smith",
        "ticket_type_id": "TICKET_TYPE_ID",
        "session_ids": ["aVSyJswkTGC4o1gqVogTaA", "SPbxN1VwSrygHG0N6T2YtQ"],
        "send_notification": true,
        "fast_join": false
      }
    ]
  }'
```

### Python

```python
def register_for_specific_sessions(event_id, ticket_type_id, attendee_email,
                                   first_name, last_name, session_ids):
    """Register attendee for selected sessions only"""
    tickets_url = f"https://api.zoom.us/v2/zoom_events/events/{event_id}/tickets"
    payload = {
        "tickets": [{
            "email": attendee_email,
            "first_name": first_name,
            "last_name": last_name,
            "ticket_type_id": ticket_type_id,
            "session_ids": session_ids,  # Array of specific session IDs
            "send_notification": True,
            "fast_join": False
        }]
    }

    response = requests.post(tickets_url, headers=headers, json=payload)
    response.raise_for_status()  # Check for HTTP errors
    ticket_data = response.json()
    print(f"Registered {attendee_email} for {len(session_ids)} sessions")
    return ticket_data['tickets'][0]['ticket_id']

# Example: Register for weeks 1 and 2 only
session_ids_to_register = ["aVSyJswkTGC4o1gqVogTaA", "SPbxN1VwSrygHG0N6T2YtQ"]
ticket_id = register_for_specific_sessions(
    event_id,
    ticket_type_id,
    "john.smith@company.com",
    "John",
    "Smith",
    session_ids_to_register
)
```

### JavaScript

```javascript
async function registerForSpecificSessions(
    eventId,
    ticketTypeId,
    email,
    firstName,
    lastName,
    sessionIds,
) {
    try {
        const response = await axios.post(
            `https://api.zoom.us/v2/zoom_events/events/${eventId}/tickets`,
            {
                tickets: [
                    {
                        email: email,
                        first_name: firstName,
                        last_name: lastName,
                        ticket_type_id: ticketTypeId,
                        session_ids: sessionIds, // Array of specific session IDs
                        send_notification: true,
                        fast_join: false,
                    },
                ],
            },
            {
                headers: {
                    "Content-Type": "application/json",
                    Authorization: "Bearer YOUR_ACCESS_TOKEN",
                },
            },
        );

        console.log(`Registered ${email} for ${sessionIds.length} sessions`);
        return response.data.tickets[0].ticket_id;
    } catch (error) {
        console.error(
            "Failed to register attendee for specific sessions:",
            error.response?.data || error.message,
        );
        throw error;
    }
}

// Example: Register for first 3 training sessions
const sessionIds = [
    "aVSyJswkTGC4o1gqVogTaA",
    "SPbxN1VwSrygHG0N6T2YtQ",
    "xYz123AbcDef",
];
await registerForSpecificSessions(
    trainingEventId,
    ticketTypeId,
    "john.smith@company.com",
    "John",
    "Smith",
    sessionIds,
);
```

---

## Recurrence pattern reference

### Weekly patterns

**Every Monday**

```json
{
    "type": 2,
    "repeat_interval": 1,
    "monthly_week_day": 1,
    "end_times": 12,
    "duration": 60
}
```

**Every Tuesday and Thursday (not directly supported—use two events):**

Weekly recurrence only supports one day per event. Create two separate recurring events for Tuesday and Thursday.

**Bi-weekly (every 2 weeks) on Fridays**

```json
{
    "type": 2,
    "repeat_interval": 2,
    "monthly_week_day": 5,
    "end_times": 6,
    "duration": 45
}
```

---

### Monthly patterns

**First Monday of each month**

```json
{
    "type": 3,
    "repeat_interval": 1,
    "monthly_week": 1,
    "monthly_week_day": 1,
    "end_times": 12,
    "duration": 90
}
```

**Third Wednesday of each month**

```json
{
    "type": 3,
    "repeat_interval": 1,
    "monthly_week": 3,
    "monthly_week_day": 3,
    "end_times": 6,
    "duration": 120
}
```

**Every 2 months (bi-monthly) on second Friday**

```json
{
    "type": 3,
    "repeat_interval": 2,
    "monthly_week": 2,
    "monthly_week_day": 5,
    "end_times": 6,
    "duration": 60
}
```

---

### Daily patterns

**Every weekday for 5 days**

```json
{
    "type": 1,
    "repeat_interval": 1,
    "end_times": 5,
    "duration": 30
}
```

**Every day for 10 days**

```json
{
    "type": 1,
    "repeat_interval": 1,
    "end_times": 10,
    "duration": 45
}
```

---

## Registration option comparison

| Option              | Use Case                                    | Behavior                                    | Example                                |
| ------------------- | ------------------------------------------- | ------------------------------------------- | -------------------------------------- |
| `all_sessions`      | Series attendance required                  | One ticket grants access to all occurrences | Team standup (attend all 12 weeks)     |
| `single_session`    | Attendees pick one occurrence               | Separate ticket per session                 | Drop-in training (attend week 2 only)  |
| `multiple_sessions` | Attendees select multiple specific sessions | One ticket for selected occurrences         | Flexible series (attend weeks 1, 3, 5) |

---

## Advanced management operations

### Update a single occurrence

To modify one occurrence without affecting others, use the PATCH endpoint with the specific `session_id`.

### Python Example

```python
def update_single_occurrence(event_id, session_id, new_start_time):
    """Update start time for one occurrence"""
    url = f"https://api.zoom.us/v2/zoom_events/events/{event_id}/sessions/{session_id}"
    payload = {
        "start_time": new_start_time
    }

    response = requests.patch(url, headers=headers, json=payload)
    if response.status_code == 204:
        print(f"Updated session {session_id}")
    return response.status_code

# Move Week 3 meeting from Monday to Tuesday
update_single_occurrence(
    event_id,
    "xYz789SessionId",
    "2025-01-21T15:00:00Z"
)
```

---

### Cancel/Delete a Single Occurrence

### Python Example

```python
def cancel_single_occurrence(event_id, session_id):
    """Cancel one occurrence without deleting the entire series"""
    url = f"https://api.zoom.us/v2/zoom_events/events/{event_id}/sessions/{session_id}"

    response = requests.delete(url, headers=headers)
    if response.status_code == 204:
        print(f"Cancelled session {session_id}")
    return response.status_code

# Cancel Week 4 due to holiday
cancel_single_occurrence(event_id, "cancelThisSessionId")
```

---

## Verification checklist

After completing this tutorial, verify your recurring event:

-   Event created with correct recurrence pattern (weekly/monthly/daily)
-   Correct number of occurrences generated (check via GET /sessions)
-   Ticket type created (even if free)
-   Access link configured with appropriate `recurring_registration_option`
-   Event published successfully
-   Registration URL accessible
-   Test registration completes for intended sessions
-   Email notifications sent to registrants

---

## Common error codes

| Code     | HTTP  | Meaning                        | Solution                                                                                                   |
| -------- | ----- | ------------------------------ | ---------------------------------------------------------------------------------------------------------- |
| `26201`  | `403` | No permission on hub           | Verify you have host role in the hub                                                                       |
| `26202`  | `400` | Schedule exceeds 6 days        | This error is for multi-session events, not recurring. Use `RECURRING` event type with recurrence patterns |
| `26203`  | `400` | `end_time` before `start_time` | Check your timezone conversions and time ordering                                                          |
| `26204`  | `400` | Calendar missing times         | Provide `start_time` in the request                                                                        |
| `260207` | `400` | Hub unsupported for event type | Verify hub supports recurring events                                                                       |
| `260200` | `403` | Event access denied            | Check authentication and permissions                                                                       |
| `2002`   | `404` | Event not found                | Verify `event_id` is correct                                                                               |
| `26501`  | `400` | Event not published            | Publish event before creating tickets                                                                      |
| `26502`  | `400` | Invalid ticket type ID         | Check `ticket_type_id` value                                                                               |

---

## FAQ

### Can I have more than 60 occurrences?

No. The maximum is 60 occurrences per series.

### Can occurrences run at the same time (concurrent sessions)?

No. Recurring events are sequential by design. All occurrences follow the recurrence pattern timeline. For concurrent sessions, use a Multi-Session (CONFERENCE) event instead.

### Can I change the recurrence pattern after publishing?

No. The recurrence pattern is locked at creation. To change the pattern, you must delete the event and create a new one with the updated recurrence settings.

### Results if I delete one occurrence?

Only that specific occurrence is cancelled. The remaining occurrences continue as scheduled. Registrants for that session receive cancellation notifications.

### How do I handle holidays or exceptions?

Use the DELETE `/events/{eventId}/sessions/{sessionId}` endpoint to cancel individual occurrences. You cannot automatically skip holidays—manual deletion is required.

### Can attendees switch which sessions they're registered for?

No.You cannot update an existing ticket with new session assignments. To change sessions, delete the ticket and create a new one using POST `/events/{eventId}/tickets` with the desired `session_ids`.

### How are analytics reported for recurring events?

Analytics aggregate across all occurrences at the series level (total registrations, total attendance) with per-occurrence breakdowns available via session-specific reports.

---

## What you have accomplished

You now know how to:

-   Create recurring event series with weekly, monthly, and daily patterns.
-   Configure registration options for series-level or per-occurrence attendance.
-   List and verify all generated occurrences.
-   Register attendees for entire series or specific sessions.
-   Update or cancel individual occurrences without affecting the series.
-   Handle common errors and edge cases.
