# MeetStream Guide: Calendar Integration & Auto-Scheduling

This guide explains how to connect your Google Calendar to MeetStream so bots can automatically join your meetings — no manual API calls required.

Applies to: **Google Meet, Zoom, Microsoft Teams** meetings on your Google Calendar.
Support: docs.meetstream.ai • API: api.meetstream.ai

---

## What you get with Calendar Integration

Once connected, MeetStream can:

- **See your upcoming meetings** — synced directly from Google Calendar
- **Schedule bots for specific meetings** — one click or one API call
- **Auto-schedule bots for all meetings** — hands-free, every day
- **Handle recurring meetings** — automatically reschedule bots for the next occurrence
- **React to calendar changes** — if a meeting is rescheduled or cancelled, MeetStream updates the bot schedule

---

## 1) Get your Google OAuth credentials

Before connecting your calendar, you need three things from Google: a **Client ID**, **Client Secret**, and **Refresh Token**. Follow these steps to get them.

### Step 1: Create a Google Cloud project and OAuth credentials

1. Go to the [Google Cloud Console](https://console.cloud.google.com/).
2. Create a new project (or use an existing one).
3. Navigate to **APIs & Services → Library** and enable the **Google Calendar API**.
4. Go to **APIs & Services → Credentials**.
5. Click **Create Credentials → OAuth 2.0 Client ID**.
6. Set the application type to **Web application**.
7. Under **Authorized redirect URIs**, add:
   ```
   http://localhost:3000/api/google/oauth-callback
   ```
8. Click **Create** and copy your **Client ID** and **Client Secret**.

### Step 2: Run the OAuth helper to get your refresh token

MeetStream provides a lightweight Node.js helper that runs the OAuth consent flow locally and returns your refresh token.

**Prerequisites:** Node.js installed on your machine.

1. Create a `.env` file in the project root with your credentials:

```
GOOGLE_CLIENT_ID=<YOUR_CLIENT_ID>
GOOGLE_CLIENT_SECRET=<YOUR_CLIENT_SECRET>
```

2. Install dependencies

```bash
npm install express googleapis dotenv
```

  3. Create the server.js file and paste this code

```typescript
const express = require('express');
const { google } = require('googleapis');
require('dotenv').config();

const app = express();
const PORT = 3000;
const REDIRECT_URI = `http://localhost:${PORT}/api/google/oauth-callback`;

const oauth2Client = new google.auth.OAuth2(
  process.env.GOOGLE_CLIENT_ID,
  process.env.GOOGLE_CLIENT_SECRET,
  REDIRECT_URI
);

const scopes = [
  'https://www.googleapis.com/auth/calendar.events.readonly',
  'https://www.googleapis.com/auth/calendar.readonly',
  'https://www.googleapis.com/auth/userinfo.email',
  'https://www.googleapis.com/auth/userinfo.profile',
];

app.get('/', (_req, res) => {
  const authUrl = oauth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: scopes,
    prompt: 'consent',
    include_granted_scopes: true,
  });
  res.redirect(authUrl);
});

app.get('/api/google/oauth-callback', async (req, res) => {
  const { code, error } = req.query;

  if (error) {
    return res.status(400).send(`<h1>OAuth Error</h1><p>${error}</p>`);
  }
  if (!code) {
    return res.status(400).send('<h1>Missing authorization code</h1>');
  }

  try {
    const { tokens } = await oauth2Client.getToken(code);

    res.send(`
      <h1>Success!</h1>
      <h3>Refresh Token</h3>
      <pre style="background:#f4f4f4;padding:12px;word-break:break-all">${tokens.refresh_token || 'Not returned — revoke access at https://myaccount.google.com/permissions and try again'}</pre>
      <h3>Access Token</h3>
      <pre style="background:#f4f4f4;padding:12px;word-break:break-all">${tokens.access_token}</pre>
      <h3>Expiry</h3>
      <p>${new Date(tokens.expiry_date).toISOString()}</p>
      <hr>
      <p>Copy the refresh token into your <code>.env</code> file, then stop this server.</p>
    `);

    console.log('\n=== TOKENS ===');
    console.log('Refresh Token:', tokens.refresh_token);
    console.log('Access Token:', tokens.access_token);
    console.log('Expiry:', new Date(tokens.expiry_date).toISOString());
  } catch (err) {
    res.status(500).send(`<h1>Token Exchange Failed</h1><pre>${err.message}</pre>`);
  }
});

app.listen(PORT, () => {
  console.log(`Open http://localhost:${PORT} to start the OAuth flow`);
  console.log(`Redirect URI: ${REDIRECT_URI}`);
  console.log('\nMake sure this redirect URI is added in Google Cloud Console → Credentials → OAuth 2.0 Client → Authorized redirect URIs');
});
```

   4. Start the helper server

```bash
node server.js
```

You'll see:

```
Open http://localhost:3000 to start the OAuth flow
Redirect URI: http://localhost:3000/api/google/oauth-callback

Make sure this redirect URI is added in Google Cloud Console → Credentials → OAuth 2.0 Client → Authorized redirect URIs
```

3. Open [**http://localhost:3000**](http://localhost:3000) in your browser.
4. Sign in with your Google account and grant calendar access.
5. After authorization, the page displays your **Refresh Token** and **Access Token**.
6. Copy the **Refresh Token** — you'll need it in the next step.

> If the refresh token is not returned, revoke MeetStream's access at [https://myaccount.google.com/permissions](https://myaccount.google.com/permissions) and try again. Google only returns a refresh token on the first consent or after a revoke.

### Required scopes

The helper requests these scopes (read-only calendar access + basic profile):

| Scope | Purpose |
|---|---|
| `calendar.events.readonly` | Read calendar events |
| `calendar.readonly` | Read calendar list |
| `userinfo.email` | Identify the Google account |
| `userinfo.profile` | Basic profile info |

You now have everything you need: **Client ID**, **Client Secret**, and **Refresh Token**.

---

## 2) Connect your Google Calendar

With your credentials ready, connect your Google Calendar to MeetStream.

### API endpoint

```
POST https://api.meetstream.ai/api/v1/calendar/create_calendar
```

### Request body

```json
{
  "google_client_id": "<YOUR_GOOGLE_CLIENT_ID>",
  "google_client_secret": "<YOUR_GOOGLE_CLIENT_SECRET>",
  "google_refresh_token": "<YOUR_GOOGLE_REFRESH_TOKEN>"
}
```

### What happens behind the scenes

1. MeetStream validates your credentials by refreshing the access token with Google.
2. Fetches all your calendars (primary, secondary, shared).
3. Stores your credentials securely (encrypted at rest).
4. Sets up **push notification channels** on your calendars — so MeetStream is notified in real time when events are created, updated, or cancelled.

### Example cURL

```bash
curl -X POST "https://api.meetstream.ai/api/v1/calendar/create_calendar" \
  -H "Authorization: Token <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "google_client_id": "<YOUR_GOOGLE_CLIENT_ID>",
    "google_client_secret": "<YOUR_GOOGLE_CLIENT_SECRET>",
    "google_refresh_token": "<YOUR_GOOGLE_REFRESH_TOKEN>"
  }'
```

### Response

You'll receive a list of your calendars and the primary calendar ID:

```json
{
  "calendars": [
    {
      "id": "user@example.com",
      "summary": "My Calendar",
      "primary": true
    },
    {
      "id": "team-meetings@group.calendar.google.com",
      "summary": "Team Meetings",
      "primary": false
    }
  ],
  "primary_calendar_id": "user@example.com"
}
```

---

## 3) View your calendars

After connecting, you can list all calendars linked to your account.

### API endpoint

```
GET https://api.meetstream.ai/api/v1/calendar/calendars
```

### Example cURL

```bash
curl -X GET "https://api.meetstream.ai/api/v1/calendar/calendars" \
  -H "Authorization: Token <YOUR_API_KEY>"
```

---

## 4) Sync and view your events

MeetStream syncs events from Google Calendar and stores them locally. It detects meeting links for supported platforms (Google Meet, Zoom, Microsoft Teams, and others).

### API endpoint — Sync events from Google Calendar

```
GET https://api.meetstream.ai/api/v1/calendar/events
```

This endpoint syncs with Google Calendar (using incremental sync for speed) and returns your events with their current bot schedule status.

### Query parameters

| Parameter | Description |
|---|---|
| `calendar_id` | (Optional) Specific calendar to sync. Defaults to primary calendar. |

### Example cURL

```bash
curl -X GET "https://api.meetstream.ai/api/v1/calendar/events" \
  -H "Authorization: Token <YOUR_API_KEY>"
```

### API endpoint — Get events from local database only

```
GET https://api.meetstream.ai/api/v1/calendar/get_events
```

This is faster since it skips the Google API call and returns events already synced to MeetStream.

### Event fields you'll receive

Each event includes:

| Field | Description |
|---|---|
| `event_id` | MeetStream's internal event ID |
| `google_event_id` | Original Google Calendar event ID |
| `summary` | Event title |
| `start_time` / `end_time` | ISO 8601 timestamps |
| `meeting_link` | Detected meeting URL (if any) |
| `meeting_platform` | `google_meet`, `zoom`, `microsoft_teams`, etc. |
| `is_recurring` | Whether this is a recurring event |
| `bot_join_status` | Whether a bot is scheduled for this event |
| `schedule_status` | `scheduled`, `unscheduled`, or `cancelled` |

> MeetStream automatically detects meeting links from Google Meet, Zoom, Microsoft Teams, Webex, GoToMeeting, BlueJeans, and Whereby.

---

## 5) Schedule a bot for a specific meeting

Pick a meeting from your synced events and schedule a bot for it.

### API endpoint

```
POST https://api.meetstream.ai/api/v1/calendar/schedule/{event_id}
```

### Request body

```json
{
  "bot_config": {
    "video_required": false,
    "callback_url": "https://your-domain.com/webhooks/meetstream"
  }
}
```

The `bot_config` accepts the same fields you'd normally pass to the Create Bot API. MeetStream will create an **EventBridge schedule** that launches the bot **3 minutes before the meeting starts**.

### Example cURL

```bash
curl -X POST "https://api.meetstream.ai/api/v1/calendar/schedule/evt_abc123" \
  -H "Authorization: Token <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "bot_config": {
      "video_required": false,
      "callback_url": "https://your-domain.com/webhooks/meetstream"
    }
  }'
```

### Recurring events

If the event is recurring, MeetStream can schedule bots for **all future occurrences** automatically. You can also schedule a bot for a specific occurrence by passing an `occurrence_date`.

### Deduplication

If you schedule a bot for the same event twice, MeetStream won't create a duplicate — it updates the existing schedule with the latest bot configuration.

---

## 6) Unschedule a bot

Cancel a scheduled bot for a specific event.

### API endpoint

```
DELETE https://api.meetstream.ai/api/v1/calendar/schedule/{event_id}
```

### Example cURL

```bash
curl -X DELETE "https://api.meetstream.ai/api/v1/calendar/schedule/evt_abc123" \
  -H "Authorization: Token <YOUR_API_KEY>"
```

This cancels the EventBridge schedule and updates the event's status to `unscheduled`.

---

## 7) Manage scheduled bots

View, update, or delete your scheduled bots across all events.

### List all scheduled bots

```
GET https://api.meetstream.ai/api/v1/calendar/scheduled_bots
```

### Update a scheduled bot

```
PATCH https://api.meetstream.ai/api/v1/calendar/scheduled_bots/{bot_id}
```

You can update the scheduled join time, name, or custom attributes.

### Delete a specific scheduled bot

```
DELETE https://api.meetstream.ai/api/v1/calendar/scheduled_bots/{bot_id}
```

---

## 8) Enable auto-scheduling

Auto-scheduling is a hands-free mode: MeetStream scans your calendar every 24 hours and automatically schedules bots for all upcoming meetings that have a meeting link.

### Enable auto-scheduling

```
POST https://api.meetstream.ai/api/v1/calendar/auto-schedule/enable
```

### Request body

Provide a default bot configuration that will be used for all auto-scheduled bots:

```json
{
  "default_bot_config": {
    "video_required": false,
    "callback_url": "https://your-domain.com/webhooks/meetstream"
  }
}
```

### Example cURL

```bash
curl -X POST "https://api.meetstream.ai/api/v1/calendar/auto-schedule/enable" \
  -H "Authorization: Token <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "default_bot_config": {
      "video_required": false,
      "callback_url": "https://your-domain.com/webhooks/meetstream"
    }
  }'
```

### Disable auto-scheduling

```
POST https://api.meetstream.ai/api/v1/calendar/auto-schedule/disable
```

### Check auto-schedule settings

```
GET https://api.meetstream.ai/api/v1/calendar/auto-schedule/settings
```

### How auto-scheduling works

1. A background job runs **every 24 hours**.
2. It finds all users who have auto-scheduling enabled.
3. For each user, it looks at upcoming events (next 24 hours) that have meeting links.
4. It skips events that already have a bot scheduled (using deduplication keys).
5. It creates bot schedules using your `default_bot_config`.

---

## 9) Recurring event auto-rescheduling

For recurring meetings (weekly standups, bi-weekly syncs, etc.), MeetStream can automatically schedule a bot for the **next occurrence** after each meeting ends.

### How it works

1. A bot joins a recurring meeting.
2. After the meeting, MeetStream detects it was a recurring event.
3. It calculates the next occurrence from the event's recurrence rule (RRULE).
4. A new bot is automatically scheduled for the next occurrence using the same configuration.

This continues indefinitely — every recurring meeting gets a bot, without manual intervention.

### Toggle auto-rescheduling for a recurring event

You can enable or disable this per event:

```
POST https://api.meetstream.ai/api/v1/calendar/toggle-recurrence
```

```json
{
  "event_id": "evt_abc123",
  "recurring_enabled": true
}
```

---

## 10) Real-time calendar change detection

When you connect your calendar, MeetStream sets up **Google Calendar push notifications** (watch channels). This means:

- **Meeting rescheduled?** → MeetStream updates the bot's scheduled join time automatically.
- **Meeting cancelled?** → MeetStream cancels the scheduled bot.
- **New meeting added?** → If auto-scheduling is enabled, a bot is scheduled for it.

You don't need to re-sync manually — changes are picked up in real time via webhooks from Google.

---

## 11) Disconnect your calendar

To remove the calendar integration entirely:

### API endpoint

```
DELETE https://api.meetstream.ai/api/v1/calendar/disconnect
```

### Example cURL

```bash
curl -X DELETE "https://api.meetstream.ai/api/v1/calendar/disconnect" \
  -H "Authorization: Token <YOUR_API_KEY>"
```

### What this does

1. Stops all Google Calendar watch channels (no more push notifications).
2. Cancels all pending bot schedules.
3. Deletes all synced event data.
4. Removes stored Google OAuth credentials.

---

## End-to-end setup checklist

Here's the full flow to get calendar integration running:

1. **Get credentials** → Create a Google Cloud project, enable the Calendar API, and run the OAuth helper to get your refresh token
2. **Connect** → `POST /calendar/create_calendar` with your Google OAuth credentials
3. **Sync** → `GET /calendar/events` to pull in your meetings
4. **Schedule** → `POST /calendar/schedule/{event_id}` to add a bot to a specific meeting
5. **Or auto-schedule** → `POST /calendar/auto-schedule/enable` to cover all meetings automatically
6. **Recurring** → Toggle `recurring_enabled` on recurring events for perpetual rescheduling
7. **Relax** → MeetStream handles calendar changes, rescheduling, and bot creation from here

---

## API quick reference

| Method | Endpoint | Description |
|---|---|---|
| POST | `/api/v1/calendar/create_calendar` | Connect Google Calendar |
| DELETE | `/api/v1/calendar/disconnect` | Disconnect calendar |
| GET | `/api/v1/calendar/calendars` | List connected calendars |
| GET | `/api/v1/calendar/events` | Sync & list events |
| GET | `/api/v1/calendar/get_events` | List events (local DB only) |
| POST | `/api/v1/calendar/schedule/{event_id}` | Schedule a bot for an event |
| DELETE | `/api/v1/calendar/schedule/{event_id}` | Unschedule a bot |
| GET | `/api/v1/calendar/scheduled_bots` | List scheduled bots |
| PATCH | `/api/v1/calendar/scheduled_bots/{bot_id}` | Update a scheduled bot |
| DELETE | `/api/v1/calendar/scheduled_bots/{bot_id}` | Delete a scheduled bot |
| POST | `/api/v1/calendar/auto-schedule/enable` | Enable auto-scheduling |
| POST | `/api/v1/calendar/auto-schedule/disable` | Disable auto-scheduling |
| GET | `/api/v1/calendar/auto-schedule/settings` | Get auto-schedule settings |
| POST | `/api/v1/calendar/auto-reschedule` | Reschedule next recurring occurrence |
| POST | `/api/v1/calendar/toggle-recurrence` | Toggle recurring auto-reschedule |

---

## FAQ

### Which calendar providers are supported?

Currently, **Google Calendar** is the only supported calendar provider. Your Google Calendar can contain meetings from any platform — MeetStream detects Google Meet, Zoom, Microsoft Teams, Webex, GoToMeeting, BlueJeans, and Whereby links.

### Do I need a Google Workspace account?

No. Any Google account with Google Calendar works — personal Gmail accounts and Google Workspace accounts are both supported.

### How do I get a Google OAuth2 refresh token?

Follow **Section 1** of this guide — create a Google Cloud project with the Calendar API enabled, set up OAuth 2.0 credentials, and run the provided OAuth helper (`node server.js`) to complete the consent flow. The helper displays your refresh token in the browser.

### How far in advance does auto-scheduling look?

The auto-schedule job runs every 24 hours and schedules bots for meetings happening in the **next 24 hours**. Meetings further out will be picked up in subsequent runs.

### When does the bot join relative to the meeting start time?

Bots are scheduled to join **3 minutes before** the meeting's start time.

### What happens if I reschedule a meeting in Google Calendar?

MeetStream receives a real-time push notification from Google and automatically updates the bot's scheduled join time to match the new meeting time.

### What if I cancel a meeting?

MeetStream detects the cancellation and cancels the corresponding bot schedule. No bot will be created.

### Can I schedule bots for meetings without a meeting link?

No. MeetStream requires a valid meeting link (Google Meet, Zoom, Teams, etc.) to join. Events without a detected meeting link are skipped.

### What happens if I schedule a bot for the same event twice?

MeetStream deduplicates by event. The second call updates the existing schedule with the new bot configuration — no duplicate bots are created.

### Can I use different bot configurations for different meetings?

Yes. When you manually schedule a bot via `/calendar/schedule/{event_id}`, you provide the `bot_config` per event. Auto-scheduling uses your `default_bot_config` for all meetings, but you can override individual events by scheduling them manually.

### Does auto-rescheduling work with all recurrence patterns?

MeetStream supports standard iCalendar recurrence rules (RRULE) — daily, weekly, bi-weekly, monthly, yearly, and custom patterns. The next occurrence is calculated from the event's recurrence rule.

### How do I stop a recurring event from being rescheduled?

Use the toggle endpoint: `POST /calendar/toggle-recurrence` with `recurring_enabled: false` for that event.

### What data is deleted when I disconnect my calendar?

Everything: watch channels are stopped, pending bot schedules are cancelled, synced event data is deleted, and Google OAuth credentials are removed from storage. This is irreversible.

### Is my Google Calendar data stored securely?

Yes. OAuth credentials are stored in AWS Systems Manager Parameter Store as encrypted `SecureString` parameters. Event data is stored in DynamoDB. MeetStream does not store your Google password.

---

For webhook event handling, see the [Webhook Events Guide](/guides/webhooks/webhooks-and-events).
For creating your first bot without calendar integration, see the [First Bot Quickstart](/guides/get-started/create-your-first-bot).
