MeetStream Guide: Outlook Calendar Integration & Auto-Scheduling
MeetStream Guide: Outlook Calendar Integration & Auto-Scheduling
This guide explains how to connect Microsoft Outlook / Microsoft 365 Calendar accounts to MeetStream so bots can automatically join your meetings — no manual API calls required.
Applies to: Google Meet, Zoom, Microsoft Teams meetings on your Outlook Calendar. Support: docs.meetstream.ai | API: api.meetstream.ai
What you get with Calendar Integration
Once connected, MeetStream can:
- Connect many Microsoft accounts per user — one MeetStream user can attach a personal Outlook.com account, a work Microsoft 365 tenant, a contractor account in a third tenant, and so on. Up to 200 per user (Google + Outlook combined).
- See upcoming meetings across every connected account — synced directly from Microsoft Graph with per-account delta tokens for speed.
- Schedule bots for specific meetings — one click or one API call.
- Auto-schedule bots for all meetings — hands-free, every day, across every connected account.
- 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. Notifications are routed per-account so one tenant’s churn never disturbs another’s.
- Manage scheduled bots — list, update, or delete scheduled bots at any time.
- Disconnect one account at a time — remove a single Microsoft account without touching the others.
- Keep subscriptions alive — Microsoft Graph change notification subscriptions are automatically renewed before they expire.
Mental model. A MeetStream user owns a set of calendar connections. Each connection is uniquely identified by a
(provider, account_id)pair — for Outlook,account_idis the account’s primary email (e.g.jane@acme.com) as returned by Microsoft Graph’smeendpoint. Every endpoint in this guide either operates across all of a MeetStream user’s connections (the default) or scopes to a single connection via?provider=&account_id=query parameters or a path parameter. The provider key isoutlookeverywhere; the aliasmicrosoftis accepted too. If you only ever connect one Outlook account per MeetStream user, the multi-account surface is invisible to you — everything just works.
1) Get your Microsoft OAuth credentials
Before connecting your calendar, you need three things from Microsoft: a Client ID, Client Secret, and Refresh Token. Follow these steps to get them.
Step 1: Register an app in Azure Portal
- Go to the Azure Portal.
- Navigate to Azure Active Directory > App registrations.
- Click New registration.
- Give your app a name (e.g. “MeetStream Calendar”).
- Under Supported account types, select Accounts in any organizational directory and personal Microsoft accounts.
- Under Redirect URI, select Web and enter:
- Click Register.
- On the app overview page, copy your Application (client) ID — this is your Client ID.
Step 2: Create a client secret
- In your app registration, go to Certificates & secrets.
- Click New client secret.
- Give it a description and choose an expiry period.
- Click Add and immediately copy the Value — this is your Client Secret. It won’t be shown again.
Step 3: Add Microsoft Graph API permissions
- In your app registration, go to API permissions.
- Click Add a permission > Microsoft Graph > Delegated permissions.
- Add the following permissions:
- Click Grant admin consent if you have admin rights, or ask your tenant admin to grant consent.
Step 4: 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.
- Create a
.envfile in the project root with your credentials:
- Install dependencies and start the helper server:
You’ll see:
- Open http://localhost:3000 in your browser.
- Sign in with your Microsoft account and grant calendar access.
- After authorization, the page displays your Refresh Token and Access Token.
- Copy the Refresh Token — you’ll need it in the next step.
If the refresh token is not returned, make sure
offline_accessis included in your permissions and that you clicked Accept on the consent screen. Microsoft only returns a refresh token whenoffline_accessis in the requested scope.
Required scopes
The helper requests these scopes:
You now have everything you need: Client ID, Client Secret, and Refresh Token.
2) Connect an Outlook Calendar account
Each call to this endpoint connects one Microsoft account. To attach a second, third, or twentieth account to the same MeetStream user, call it again with a different refresh token. MeetStream resolves the account from Microsoft Graph’s me endpoint and stores each connection separately.
API endpoint
Request body
Optional:
What happens behind the scenes
- MeetStream exchanges the refresh token at Microsoft’s OAuth endpoint to validate the credentials.
- Calls Microsoft Graph’s
meendpoint to resolve the account’s primary email — that email becomes theaccount_idfor this connection. - Fetches all calendars on that account (default, secondary, shared) via Microsoft Graph.
- Stores your credentials securely (encrypted at rest in AWS SSM Parameter Store) under a per-account path, isolated from any other accounts you’ve connected.
- Registers Microsoft Graph change notification subscriptions on every calendar on that account, pointing at a per-account webhook URL with a per-subscription
clientStatesecret so notifications for this account never cross-contaminate any other connection on the same user. - Returns the connection record (no token material).
Example cURL
Response
Connecting a second account
Run the OAuth helper again signed in as the second Microsoft account, get a new refresh token, then call POST /create_outlook_calendar again with that token. The new account is added to the MeetStream user; existing connections — Outlook or Google — are untouched. There’s no separate “add account” endpoint, and there’s no provider mode: the same endpoint handles both Microsoft and Google credentials based on which fields you send.
Reconnecting an existing account (token rotation)
If the same account_id is already connected for this MeetStream user, the call fails with 409 Conflict by default:
Pass "replace": true in the body to rotate the refresh token / refresh the scopes for an already-connected account in place:
This is the right flow when your Azure client secret rotated or when you’ve extended the granted scopes — the credentials are updated without losing event history or notification subscriptions for that account.
Limits
At the limit, POST /create_outlook_calendar returns 400 with "Maximum of 200 outlook calendar connections per user reached…". Disconnect one before adding another (see §11), or contact support to raise the limit.
3) View your connections and calendars
You have two read endpoints: one for the connections themselves (which Microsoft accounts are attached, with metadata) and one for the calendars within those connections (live from Graph).
3a) List your calendar connections
Returns every connection on the authenticated MeetStream user, grouped by provider. No token material is ever returned.
Inspect one connection
{provider} is outlook (the alias microsoft is accepted). {account_id} must be URL-encoded (@ becomes %40, etc.).
Returns a single connection record like one of the entries above. 404 if the user has no connection for that account.
3b) List the calendars inside those connections
Returns calendars across every connected account by default (Outlook + Google), with a per-connection summary so you can group them in your UI.
Query parameters
Example cURL
Response
This is a live call to the Microsoft Graph API and always returns the latest calendar list. Each entry carries
providerandaccount_idso the dashboard / client can tell which connection it came from.If one connection fails to respond (revoked token, Graph throttling, expired client secret), the call still succeeds and surfaces the error under a
partial_errorsmap keyed by"{provider}::{account_id}".
4) Sync and view your events
MeetStream syncs events from every connected Outlook Calendar account and stores them locally. It detects meeting links for supported platforms (Google Meet, Zoom, Microsoft Teams, Webex, GoToMeeting, BlueJeans, and Whereby).
Sync events from Outlook Calendar
This is the primary events endpoint. It syncs with Outlook Calendar (using per-account Microsoft Graph delta tokens for speed), stores events in MeetStream’s database, and returns them with pagination and linked bot information. By default, events from every connected account on the MeetStream user (Outlook + Google) are returned.
Query parameters
Example cURL
Response
Every event row carries a provider and account_id so you can group / filter client-side without re-issuing scoped requests. Use the next cursor value in a subsequent request to fetch the next page:
Scoping to a single account
When the user has multiple connections, you can scope the sync + return to just one of them. This is faster (only one account is touched) and useful for per-tenant UIs.
Get events from local database only
This is a lightweight endpoint that returns events already synced to MeetStream without calling the Microsoft Graph API. Useful when you want fast reads and don’t need the latest sync. Accepts the same provider / account_id scoping query parameters as /calendar/events.
5) Schedule a bot for a specific meeting
Pick a meeting from your synced events and schedule a bot for it.
API endpoint
Path parameters
Request body
The bot_config accepts the same fields you’d normally pass to the Create Bot API.
Example cURL
Response
Scheduling options for recurring events
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
Request body (optional)
Example cURL
Response
7) Manage scheduled bots
View, update, or delete your scheduled bots across all events.
List all scheduled bots
Returns all bots with a scheduled join time in the future.
Query parameters
Example cURL
Response
Update a scheduled bot
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.
Response
Delete a specific scheduled bot
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
Request body
Provide a default bot configuration that will be used for all auto-scheduled bots:
Example cURL
Response
Disable auto-scheduling
Check auto-schedule settings
How auto-scheduling works
- A background job runs every 24 hours at midnight UTC.
- It finds all users who have auto-scheduling enabled.
- For each user, it looks at upcoming events in the next 24 hours that have a valid meeting link.
- It skips events that already have a bot scheduled (using deduplication keys).
- It creates bot schedules using your
default_bot_config. - 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 custombot_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
- A bot joins a recurring meeting.
- After the meeting ends, MeetStream detects it was a recurring event.
- It calculates the next occurrence from the event’s recurrence rule.
- 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:
Toggle auto-rescheduling for an existing event
You can enable or disable auto-rescheduling per event at any time:
Response
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:
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) as surfaced by Microsoft Graph:
10) Real-time calendar change detection
When you connect your calendar, MeetStream sets up Microsoft Graph change notification subscriptions on your calendars. This enables real-time reactions to calendar changes without polling.
What MeetStream handles automatically
How it works under the hood
- Microsoft Graph detects a change and sends an HTTPS POST to a per-account webhook URL of the form
…/api/v1/admin/schedule/users/{user_id}/{provider}/{account_id}— so a notification forjane@acme.comcannot be confused with a notification forjane.personal@outlook.comeven when both are connected to the same MeetStream user. - MeetStream validates the notification using the per-subscription
clientStatesecret issued at registration time and rejects mismatches. - MeetStream fetches updated event data from the Microsoft Graph API using the per-account delta token (one token per
account_id, isolated from every other account’s sync state). - 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
ScheduledJoinTimeis also updated soGET /scheduled_botsreflects 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.
- Single events: The existing EventBridge schedule is updated in place with the new time. The bot record’s
Only events from the account that fired the notification are touched. Changes on other connections wait for their own notifications. You don’t need to re-sync manually or make any API calls — changes are picked up in real time via webhooks from Microsoft.
Subscription auto-renewal
Microsoft Graph change notification subscriptions for calendar events expire after approximately 3 days (4,230 minutes). MeetStream automatically renews expiring subscriptions:
- A background job runs daily at 3:00 AM UTC.
- It checks all connected users’ Graph subscriptions.
- Any subscription expiring within 2 days is renewed automatically.
- No user action is required — webhooks stay active indefinitely.
This is more frequent than Google Calendar’s 30-day watch channels. MeetStream handles the renewal schedule transparently — you will not notice any gap in change detection.
11) Disconnect a calendar account
There are two disconnect endpoints. For multi-account users, prefer the per-account variant — it’s explicit about which account is being removed and leaves every other connection alone.
11a) Disconnect one account (recommended)
{provider} is outlook (the alias microsoft is accepted). {account_id} must be URL-encoded (@ → %40).
What this does
- Stops the Microsoft Graph change notification subscriptions registered for this connection (no more push notifications).
- Deletes the per-account OAuth credentials from secure storage.
- Removes the connection record from
GET /calendar/connections. - Cleans up events and scheduled bots that came from this account —
Eventsrows tagged with this(provider, account_id)are deleted; bots inScheduledstatus for those events move toCancelledwithreason: "calendar_disconnected". Bots that have already run are not touched.
Other Outlook (and Google) connections on this MeetStream user are completely unaffected.
Query parameter
Example cURL
Response
This endpoint is idempotent — re-calling it for an already-removed account returns 404 Not Found without changing state.
11b) Disconnect (legacy single-account endpoint)
Original endpoint, kept for backward compatibility. Behaviour depends on how many connections the MeetStream user has:
The 400 response includes a next_steps hint so callers know exactly what to do:
This safety check exists so that one careless API call cannot accidentally wipe out a multi-tenant user’s entire calendar history.
Both disconnect paths are irreversible. To reconnect an account, call
POST /calendar/create_outlook_calendaragain with its credentials.
End-to-end setup checklist
Here’s the full flow to get Outlook Calendar integration running:
- Get credentials — Register an app in Azure Portal, add Microsoft Graph permissions, and run the OAuth helper to get a refresh token for each Microsoft account you want to connect
- Connect —
POST /calendar/create_outlook_calendaronce per account. Each call attaches that account to the same MeetStream user. - Verify —
GET /calendar/connectionsto confirm every account you wanted is attached - Sync —
GET /calendar/eventsto pull in meetings from every connected account (or pass?provider=outlook&account_id=…to scope) - Schedule —
POST /calendar/schedule/{event_id}to add a bot to a specific meeting - Or auto-schedule —
POST /calendar/auto-schedule/enableto cover all meetings across all connected accounts automatically - Recurring — Set
recurring_event: truewhen scheduling, or usePOST /calendar/toggle-recurrence - Relax — MeetStream handles calendar changes, rescheduling, cancellations, and subscription renewal per-account from here
API quick reference
FAQ
Which calendar providers are supported?
Google Calendar and Outlook Calendar (Microsoft 365 / Outlook.com) are both supported on the same MeetStream user. Your calendar can contain meetings from any platform — MeetStream detects Google Meet, Zoom, Microsoft Teams, Webex, GoToMeeting, BlueJeans, and Whereby links. See the Google Calendar Integration Guide for the Google flow.
Do I need a Microsoft 365 (work/school) account?
No. Both personal Microsoft accounts (Outlook.com, Hotmail, Live) and Microsoft 365 work/school accounts are supported. When registering your app in Azure Portal, choose Accounts in any organizational directory and personal Microsoft accounts to cover both. You can mix and match: one MeetStream user can connect a personal Outlook.com account and multiple work tenants at the same time.
Can one MeetStream user connect multiple Microsoft accounts?
Yes — up to 200 calendar connections per MeetStream user (Outlook + Google combined). Call POST /create_outlook_calendar once per refresh token. Each connection is identified by (provider="outlook", account_id=<the account's primary email>). Use GET /calendar/connections to inspect them, and DELETE /calendar/connections/{provider}/{account_id} to remove one without affecting the others.
What happens if I call POST /create_outlook_calendar for the same Microsoft account twice?
The second call returns 409 Conflict with the existing account_id. To rotate the refresh token or refresh the scopes for an already-connected account, pass "replace": true in the request body — the credentials are updated in place without losing event history or notification subscriptions.
Can I scope GET /calendar/events to just one of my connected accounts?
Yes. Pass ?provider=outlook&account_id=<URL-encoded-email>. Without those query parameters, events from every connected account are returned (each row carries provider and account_id so you can group them client-side).
How does MeetStream avoid mixing up webhook notifications between my connected accounts?
Each connection has its own webhook URL of the form …/api/v1/admin/schedule/users/{user_id}/{provider}/{account_id}, and an opaque per-subscription clientState issued at registration. Notifications that arrive on the wrong URL or carry a mismatched clientState are rejected. Delta tokens, calendar lists, and notification subscriptions are all stored per-account in isolated storage paths.
I only have one Outlook account connected — do I need to worry about any of this multi-account stuff?
No. Every endpoint behaves the same way when you have exactly one connection — you can keep ignoring provider, account_id, and /connections. They become useful the moment you connect a second account.
How do I get a Microsoft OAuth2 refresh token?
Follow Section 1 of this guide — register an app in Azure Portal, add the required Microsoft Graph permissions, and run the provided OAuth helper (node server_microsoft.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 Outlook?
MeetStream receives a real-time push notification from Microsoft Graph 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 the Microsoft Graph notification, 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.
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 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 as returned by Microsoft Graph.
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 Microsoft Graph subscriptions expiring?
No. MeetStream automatically renews Microsoft Graph change notification subscriptions before they expire. A daily background job checks for subscriptions 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?
- Per-account disconnect (
DELETE /calendar/connections/{provider}/{account_id}): Graph subscriptions for that account are stopped, its credentials are wiped from secure storage, the connection record is removed, and (by default) events from that account along with their scheduled bots are deleted. Pass?purge_events=falseto keep the event history. Other connected accounts are completely untouched. - Full-user disconnect (
DELETE /calendar/disconnect): the legacy endpoint. When only one connection exists, it tears that one down. When multiple connections exist, it refuses (returns 400) unless you specify which account in the body — to prevent accidentally wiping out a multi-tenant user’s entire history.
Both paths are irreversible. To reconnect, call POST /calendar/create_outlook_calendar again.
Is my Outlook 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 Microsoft 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.
My client secret expired — what do I do?
Generate a new client secret in Azure Portal under your app’s Certificates & secrets, then re-run the OAuth helper to get a fresh refresh token, and call POST /calendar/create_outlook_calendar again with the new credentials. MeetStream will replace the old connection automatically.
For webhook event handling, see the Webhook Events Guide. For creating your first bot without calendar integration, see the First Bot Quickstart. For Google Calendar integration, see the Google Calendar Integration Guide.
