MeetStream Guide: Calendar Integration & Auto-Scheduling

View as Markdown

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.


What you get with Calendar Integration

Once connected, MeetStream can:

  • See your upcoming meetings — synced directly from Google Calendar with incremental sync for speed
  • 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 in real time — if a meeting is rescheduled, the bot’s join time updates automatically; if a meeting is cancelled, the bot is cancelled too
  • Manage scheduled bots — list, update, or delete scheduled bots at any time
  • Keep webhooks alive — watch channels are automatically renewed before they expire

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.
  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>
  1. Install dependencies
$npm install express googleapis dotenv
  1. Create the server.js file and paste this code
1const express = require('express');
2const { google } = require('googleapis');
3require('dotenv').config();
4
5const app = express();
6const PORT = 3000;
7const REDIRECT_URI = `http://localhost:${PORT}/api/google/oauth-callback`;
8
9const oauth2Client = new google.auth.OAuth2(
10 process.env.GOOGLE_CLIENT_ID,
11 process.env.GOOGLE_CLIENT_SECRET,
12 REDIRECT_URI
13);
14
15const scopes = [
16 'https://www.googleapis.com/auth/calendar.events.readonly',
17 'https://www.googleapis.com/auth/calendar.readonly',
18 'https://www.googleapis.com/auth/userinfo.email',
19 'https://www.googleapis.com/auth/userinfo.profile',
20];
21
22app.get('/', (_req, res) => {
23 const authUrl = oauth2Client.generateAuthUrl({
24 access_type: 'offline',
25 scope: scopes,
26 prompt: 'consent',
27 include_granted_scopes: true,
28 });
29 res.redirect(authUrl);
30});
31
32app.get('/api/google/oauth-callback', async (req, res) => {
33 const { code, error } = req.query;
34
35 if (error) {
36 return res.status(400).send(`<h1>OAuth Error</h1><p>${error}</p>`);
37 }
38 if (!code) {
39 return res.status(400).send('<h1>Missing authorization code</h1>');
40 }
41
42 try {
43 const { tokens } = await oauth2Client.getToken(code);
44
45 res.send(`
46 <h1>Success!</h1>
47 <h3>Refresh Token</h3>
48 <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>
49 <h3>Access Token</h3>
50 <pre style="background:#f4f4f4;padding:12px;word-break:break-all">${tokens.access_token}</pre>
51 <h3>Expiry</h3>
52 <p>${new Date(tokens.expiry_date).toISOString()}</p>
53 <hr>
54 <p>Copy the refresh token into your <code>.env</code> file, then stop this server.</p>
55 `);
56
57 console.log('\n=== TOKENS ===');
58 console.log('Refresh Token:', tokens.refresh_token);
59 console.log('Access Token:', tokens.access_token);
60 console.log('Expiry:', new Date(tokens.expiry_date).toISOString());
61 } catch (err) {
62 res.status(500).send(`<h1>Token Exchange Failed</h1><pre>${err.message}</pre>`);
63 }
64});
65
66app.listen(PORT, () => {
67 console.log(`Open http://localhost:${PORT} to start the OAuth flow`);
68 console.log(`Redirect URI: ${REDIRECT_URI}`);
69 console.log('\nMake sure this redirect URI is added in Google Cloud Console → Credentials → OAuth 2.0 Client → Authorized redirect URIs');
70});
  1. Start the helper server
$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
  1. Open http://localhost:3000 in your browser.
  2. Sign in with your Google account and grant calendar access.
  3. After authorization, the page displays your Refresh Token and Access Token.
  4. 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 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):

ScopePurpose
calendar.events.readonlyRead calendar events
calendar.readonlyRead calendar list
userinfo.emailIdentify the Google account
userinfo.profileBasic 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

1{
2 "google_client_id": "<YOUR_GOOGLE_CLIENT_ID>",
3 "google_client_secret": "<YOUR_GOOGLE_CLIENT_SECRET>",
4 "google_refresh_token": "<YOUR_GOOGLE_REFRESH_TOKEN>"
5}

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 in AWS SSM Parameter Store).
  4. Sets up push notification channels on all calendars you have write access to — so MeetStream is notified in real time when events are created, updated, or cancelled.
  5. Fetches your Google profile info (email, name) for display purposes.

Example cURL

$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

1{
2 "calendar_id": "google_calendar_usr_abc123",
3 "platform": "google_calendar",
4 "user_email": "user@example.com",
5 "user_name": "Jane Smith",
6 "calendars": [
7 {
8 "id": "user@example.com",
9 "summary": "My Calendar",
10 "isPrimary": true,
11 "accessRole": "owner",
12 "selected": true
13 },
14 {
15 "id": "team-meetings@group.calendar.google.com",
16 "summary": "Team Meetings",
17 "isPrimary": false,
18 "accessRole": "writer",
19 "selected": true
20 }
21 ],
22 "primary_calendar_id": "user@example.com",
23 "watch_setup": {
24 "success": true,
25 "watches_setup": 2,
26 "watch_results": [
27 {
28 "calendar_id": "user@example.com",
29 "calendar_summary": "My Calendar",
30 "channel_id": "a1b2c3d4-...",
31 "expiration": "1748000000000"
32 }
33 ],
34 "failed_calendars": []
35 },
36 "message": "Calendar connected successfully"
37}

Note: If you call this endpoint again with updated credentials, MeetStream replaces the existing connection — no need to disconnect first.


3) View your calendars

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

API endpoint

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

Example cURL

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

Response

1{
2 "calendars": [
3 {
4 "id": "user@example.com",
5 "summary": "My Calendar",
6 "isPrimary": true,
7 "accessRole": "owner",
8 "timeZone": "America/New_York",
9 "backgroundColor": "#4285f4",
10 "selected": true
11 }
12 ],
13 "total": 1,
14 "user_id": "usr_abc123"
15}

This is a live call to the Google Calendar API and always returns the latest calendar list.


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, Webex, GoToMeeting, BlueJeans, and Whereby).

Sync events from Google Calendar

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

This is the primary events endpoint. It syncs with Google Calendar (using incremental sync for speed), stores events in MeetStream’s database, and returns them with pagination and linked bot information.

Query parameters

ParameterTypeDefaultDescription
calendar_idstringprimary calendarSpecific calendar to sync
time_minISO 8601now - 1 dayStart of the time window
time_maxISO 8601now + 28 daysEnd of the time window
sync"true" / "false"autoForce a sync from Google Calendar
limitint (1-100)50Number of events per page
cursorstringPagination cursor from next field
cleanup_duplicates"true" / "false""false"Deduplicate events

Example cURL

$curl -X GET "https://api.meetstream.ai/api/v1/calendar/events?limit=20" \
> -H "Authorization: Token <YOUR_API_KEY>"

Response

1{
2 "next": "eyJFdmVudElEIjogIm...",
3 "previous": null,
4 "results": [
5 {
6 "id": "evt_abc123",
7 "start_time": "2026-04-08T15:00:00Z",
8 "end_time": "2026-04-08T16:00:00Z",
9 "calendar_id": "user@example.com",
10 "platform": "google_calendar",
11 "platform_id": "google_evt_456",
12 "ical_uid": "abc123@google.com",
13 "meeting_platform": "GMeet",
14 "meeting_url": "https://meet.google.com/abc-defg-hij",
15 "is_deleted": false,
16 "created_at": "2026-04-01T12:00:00Z",
17 "updated_at": "2026-04-07T09:30:00Z",
18 "raw": { },
19 "bots": [
20 {
21 "id": "bot_111",
22 "status": "Scheduled",
23 "scheduled_join_time": "2026-04-08T14:59:00+00:00",
24 "bot_username": "MeetStream Calendar Bot",
25 "platform": "GMeet",
26 "is_scheduled": true
27 }
28 ]
29 }
30 ],
31 "has_more": false
32}

Use the next cursor value in a subsequent request to fetch the next page:

$curl -X GET "https://api.meetstream.ai/api/v1/calendar/events?cursor=eyJFdmVudElEIjogIm..." \
> -H "Authorization: Token <YOUR_API_KEY>"

Get events from local database only

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

This is a lightweight endpoint that returns events already synced to MeetStream without calling the Google Calendar API. Useful when you want fast reads and don’t need the latest sync.

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

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}

Path parameters

ParameterDescription
event_idMeetStream event ID (the id field from the events list)

Request body

1{
2 "bot_config": {
3 "bot_name": "My Meeting Bot",
4 "audio_required": true,
5 "video_required": false,
6 "bot_message": "Bot joining to record this meeting",
7 "callback_url": "https://your-domain.com/webhooks/meetstream",
8 "transcription": {
9 "deepgram": { "model": "nova-3", "language": "en" }
10 },
11 "automatic_leave": {
12 "no_one_joined_timeout": 300,
13 "everyone_left_timeout": 60
14 },
15 "recording_config": {
16 "video_recording": false
17 }
18 }
19}

The bot_config accepts the same fields you’d normally pass to the Create Bot API.

Example cURL

$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"
> }
> }'

Response

1{
2 "scheduled": true,
3 "schedule_id": "bot-usr_abc1-bot_111aaa12",
4 "bot_id": "bot_111...",
5 "schedule_group": "gmeet",
6 "event_id": "evt_abc123",
7 "scheduled_time": "2026-04-08T14:59:00Z",
8 "bot_config": { },
9 "is_recurring_occurrence": false
10}

Scheduling options for recurring events

Body fieldTypeDefaultDescription
occurrence_dateISO 8601Schedule a bot for a specific occurrence of a recurring event
schedule_all_occurrencesboolfalseSchedule bots for all future occurrences at once
occurrence_limitint52Maximum number of occurrences to schedule when using schedule_all_occurrences
recurring_eventboolfalseEnable auto-rescheduling — after the bot joins this occurrence, automatically schedule the next one

Deduplication

If you schedule a bot for the same event twice, MeetStream returns a 409 Conflict with the existing bot’s ID. To update the bot config, use PATCH /calendar/scheduled_bots/{bot_id} instead.


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}

Request body (optional)

1{
2 "cancel_all_occurrences": false,
3 "from_date": "2026-05-01T00:00:00Z"
4}
Body fieldTypeDefaultDescription
cancel_all_occurrencesboolfalseCancel bots for all occurrences of a recurring series
from_dateISO 8601Only cancel occurrences from this date forward

Example cURL

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

Response

1{
2 "unscheduled": true,
3 "event_id": "evt_abc123",
4 "cancelled_schedules": ["bot-usr_abc1-bot_111aaa12"],
5 "schedules_cancelled": 1,
6 "bots_deleted": 1,
7 "cancel_all_occurrences": false,
8 "is_recurring_series": false
9}

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

Returns all bots with a scheduled join time in the future.

Query parameters

ParameterTypeDefaultDescription
limitint (1-100)100Maximum number of bots to return

Example cURL

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

Response

1{
2 "scheduled_bots": [
3 {
4 "bot_id": "bot_111...",
5 "platform": "GMeet",
6 "status": "Scheduled",
7 "scheduled_join_time": "2026-04-08T14:59:00+00:00",
8 "bot_username": "MeetStream Calendar Bot",
9 "meeting_link": "https://meet.google.com/abc-defg-hij",
10 "custom_attributes": {
11 "source": "calendar_integration",
12 "event_id": "google_evt_456",
13 "event_summary": "Weekly Standup"
14 },
15 "is_scheduled": true,
16 "created_at": "2026-04-07T10:00:00Z"
17 }
18 ]
19}

Update a scheduled bot

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

Update the join time, display name, or other properties of a scheduled bot.

Request body

All fields are optional — include only what you want to change.

1{
2 "scheduled_join_time": "2026-04-08T16:59:00Z",
3 "bot_username": "Updated Bot Name",
4 "custom_attributes": { "note": "VIP meeting" }
5}
Body fieldTypeDescription
scheduled_join_timeISO 8601New join time (must be in the future). Updates the EventBridge schedule too.
bot_usernamestringDisplay name for the bot in the meeting
custom_attributesobjectCustom metadata attached to the bot

Response

1{
2 "message": "Scheduled bot updated successfully",
3 "bot_id": "bot_111...",
4 "updated_fields": ["scheduled_join_time", "bot_username"],
5 "schedule_updated": true
6}

Delete a specific scheduled bot

DELETE https://api.meetstream.ai/api/v1/calendar/scheduled_bots/{bot_id}
$curl -X DELETE "https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_111..." \
> -H "Authorization: Token <YOUR_API_KEY>"
1{
2 "message": "Scheduled bot deleted successfully",
3 "bot_id": "bot_111..."
4}

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:

1{
2 "default_bot_config": {
3 "bot_name": "MeetStream Auto Bot",
4 "audio_required": true,
5 "video_required": false,
6 "callback_url": "https://your-domain.com/webhooks/meetstream",
7 "transcription": {
8 "deepgram": { "model": "nova-3", "language": "en" }
9 },
10 "automatic_leave": {
11 "no_one_joined_timeout": 300,
12 "everyone_left_timeout": 60
13 }
14 }
15}

Example cURL

$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"
> }
> }'

Response

1{
2 "message": "Auto-scheduling enabled successfully",
3 "auto_schedule_enabled": true,
4 "default_bot_config": { }
5}

Disable auto-scheduling

POST https://api.meetstream.ai/api/v1/calendar/auto-schedule/disable
$curl -X POST "https://api.meetstream.ai/api/v1/calendar/auto-schedule/disable" \
> -H "Authorization: Token <YOUR_API_KEY>"

Check auto-schedule settings

GET https://api.meetstream.ai/api/v1/calendar/auto-schedule/settings
1{
2 "auto_schedule_enabled": true,
3 "default_bot_config": {
4 "bot_name": "MeetStream Auto Bot",
5 "audio_required": true,
6 "video_required": false
7 }
8}

How auto-scheduling works

  1. A background job runs every 24 hours at midnight UTC.
  2. It finds all users who have auto-scheduling enabled.
  3. For each user, it looks at upcoming events in the next 24 hours that have a valid meeting link.
  4. It skips events that already have a bot scheduled (using deduplication keys).
  5. It creates bot schedules using your default_bot_config.
  6. Bots are scheduled to join 1 minute before the meeting starts.

You can override individual meetings by manually scheduling them with POST /calendar/schedule/{event_id} and a custom 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 ends, 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.

Enable auto-rescheduling when scheduling

When scheduling a bot for a recurring event, set recurring_event: true in the request body:

$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 },
> "recurring_event": true
> }'

Toggle auto-rescheduling for an existing event

You can enable or disable auto-rescheduling per event at any time:

POST https://api.meetstream.ai/api/v1/calendar/toggle-recurrence
1{
2 "event_id": "evt_abc123",
3 "recurring_enabled": true
4}

Response

1{
2 "event_id": "evt_abc123",
3 "recurring_enabled": true,
4 "recurrence_rule": "FREQ=WEEKLY;BYDAY=MO,WE,FR",
5 "message": "Recurrence enabled for event",
6 "summary": "Weekly Team Standup",
7 "start_time": "2026-04-07T10:00:00Z",
8 "end_time": "2026-04-07T10:30:00Z"
9}

This endpoint returns 400 if the event is not actually a recurring event (no recurrence rule).

Trigger rescheduling manually

MeetStream also exposes an endpoint to manually trigger rescheduling for the next occurrence:

POST https://api.meetstream.ai/api/v1/calendar/auto-reschedule
1{
2 "user_id": "usr_abc123",
3 "event_id": "evt_abc123",
4 "recurrence_rule": "FREQ=WEEKLY;BYDAY=MO,WE,FR",
5 "current_start_time": "2026-04-07T10:00:00Z",
6 "bot_config": { "video_required": false },
7 "meeting_link": "https://meet.google.com/abc-defg-hij"
8}

This is primarily used internally by MeetStream after a meeting ends. You typically don’t need to call it directly.

Supported recurrence patterns

MeetStream supports standard iCalendar recurrence rules (RRULE):

PatternRRULE example
DailyFREQ=DAILY
WeeklyFREQ=WEEKLY;BYDAY=MO,WE,FR
Bi-weeklyFREQ=WEEKLY;INTERVAL=2;BYDAY=TU
MonthlyFREQ=MONTHLY;BYDAY=1MO (first Monday)
YearlyFREQ=YEARLY;BYMONTH=3;BYMONTHDAY=15

10) Real-time calendar change detection

When you connect your calendar, MeetStream sets up Google Calendar push notification channels (watch channels) on all your calendars. This enables real-time reactions to calendar changes without polling.

What MeetStream handles automatically

Calendar changeWhat MeetStream does
Meeting rescheduled (time changed)Updates the bot’s EventBridge schedule and ScheduledJoinTime to match the new time. The bot joins at the correct time.
Meeting cancelled or deletedCancels the EventBridge schedule and marks the bot record as Cancelled. No bot is created.
Meeting time moved to the pastDeletes the schedule and cancels the bot (since the meeting already happened or won’t happen).
New meeting addedIf auto-scheduling is enabled, a bot is scheduled for it during the next auto-schedule run.

How it works under the hood

  1. Google Calendar detects a change and sends an HTTPS POST to MeetStream’s webhook endpoint.
  2. MeetStream refreshes the event data from Google Calendar using incremental sync.
  3. For each event that has an active bot scheduled:
    • Single events: The existing EventBridge schedule is updated in place with the new time. The bot record’s ScheduledJoinTime is also updated so GET /scheduled_bots reflects the correct time.
    • Recurring events: All existing schedules are deleted and recreated with the updated recurrence times.
    • Cancelled events: The schedule is deleted and the bot record is marked Cancelled.

You don’t need to re-sync manually or make any API calls — changes are picked up in real time via webhooks from Google.

Watch channel auto-renewal

Google Calendar watch channels expire after approximately 30 days. MeetStream automatically renews expiring channels:

  • A background job runs daily at 3:00 AM UTC.
  • It checks all connected users’ watch channels.
  • Any channel expiring within 2 days is renewed automatically.
  • No user action is required — webhooks stay active indefinitely.

11) Disconnect your calendar

To remove the calendar integration entirely:

API endpoint

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

Example cURL

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

Response

1{
2 "disconnected": true,
3 "user_id": "usr_abc123",
4 "watch_channel_stopped": true,
5 "events_deleted": 42,
6 "schedules_cancelled": 5,
7 "message": "Calendar disconnected successfully. All events and scheduled bots have been removed."
8}

What this does

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

This is irreversible. To reconnect, call POST /calendar/create_calendar again with your 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. ConnectPOST /calendar/create_calendar with your Google OAuth credentials
  3. SyncGET /calendar/events to pull in your meetings
  4. SchedulePOST /calendar/schedule/{event_id} to add a bot to a specific meeting
  5. Or auto-schedulePOST /calendar/auto-schedule/enable to cover all meetings automatically
  6. Recurring — Set recurring_event: true when scheduling, or use POST /calendar/toggle-recurrence
  7. Relax — MeetStream handles calendar changes, rescheduling, cancellations, and watch channel renewal from here

API quick reference

MethodEndpointDescription
Calendar connection
POST/api/v1/calendar/create_calendarConnect Google Calendar
DELETE/api/v1/calendar/disconnectDisconnect calendar and clean up all data
GET/api/v1/calendar/calendarsList connected calendars (live from Google)
Events
GET/api/v1/calendar/eventsSync and list events with pagination and bot info
GET/api/v1/calendar/get_eventsList events from local database only (fast)
Bot scheduling
POST/api/v1/calendar/schedule/{event_id}Schedule a bot for an event
DELETE/api/v1/calendar/schedule/{event_id}Unschedule a bot for an event
Scheduled bot management
GET/api/v1/calendar/scheduled_botsList all upcoming scheduled bots
PATCH/api/v1/calendar/scheduled_bots/{bot_id}Update a scheduled bot (time, name, attributes)
DELETE/api/v1/calendar/scheduled_bots/{bot_id}Delete a specific scheduled bot
Auto-scheduling
POST/api/v1/calendar/auto-schedule/enableEnable auto-scheduling with default bot config
POST/api/v1/calendar/auto-schedule/disableDisable auto-scheduling
GET/api/v1/calendar/auto-schedule/settingsGet current auto-schedule settings
Recurring events
POST/api/v1/calendar/auto-rescheduleTrigger rescheduling for next recurring occurrence
POST/api/v1/calendar/toggle-recurrenceEnable/disable auto-rescheduling per event

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 at midnight UTC 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 1 minute 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. Both the EventBridge schedule and the bot record in MeetStream’s database are updated, so GET /scheduled_bots always shows the correct time.

What if I cancel a meeting?

MeetStream detects the cancellation via Google Calendar sync, deletes the EventBridge schedule, and marks the bot as Cancelled. No bot will be created for a cancelled meeting.

What if a meeting is moved to a time that already passed?

MeetStream detects that the new time is in the past, deletes the EventBridge schedule, and cancels the bot record. This prevents a bot from being created for a meeting that can no longer be joined.

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 returns a 409 Conflict with the existing bot’s ID. To update the bot config, use PATCH /calendar/scheduled_bots/{bot_id}.

Can I use different bot configurations for different meetings?

Yes. When you manually schedule a bot via POST /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.

Do I need to worry about watch channels expiring?

No. MeetStream automatically renews Google Calendar watch channels before they expire. A daily background job checks for channels expiring within 2 days and renews them. Your real-time calendar sync stays active indefinitely without any action on your part.

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 with encryption at rest. MeetStream does not store your Google password.

Can I schedule bots for all occurrences of a recurring event at once?

Yes. Pass "schedule_all_occurrences": true in the request body when scheduling. You can limit the number of occurrences with "occurrence_limit" (default 52). Each occurrence gets its own bot and EventBridge schedule.

How do I schedule a bot for a specific occurrence of a recurring event?

Pass "occurrence_date": "2026-04-14T10:00:00Z" in the request body. MeetStream will schedule a bot for that specific occurrence only.

Can I cancel bots for future occurrences only?

Yes. When unscheduling a recurring event, pass "from_date": "2026-05-01T00:00:00Z" to cancel only occurrences from that date forward. Occurrences before that date keep their scheduled bots.


For webhook event handling, see the Webhook Events Guide. For creating your first bot without calendar integration, see the First Bot Quickstart.