How to Set Up a Microsoft Teams Meeting Bot with Unified

Updated:


November 7, 2026

Overview

Microsoft Teams (meeting bot) (microsoftteamsmeetingbot) is the integration that gives you a calendar_bot and calendar_botevents for Microsoft Teams meetings. It is separate from Microsoft Teams and Microsoft Teams (bot): existing connections don't change, and your end users connect Microsoft Teams (meeting bot) on its own.

A Teams meeting bot never joins the meeting and captures no media. It monitors the meeting through Microsoft Graph and reports it after the meeting ends:

  • the call record: start and end times, participants, and BOT_STATUS_CHANGE to CALL_ENDED
  • PARTICIPANT_JOINED / PARTICIPANT_LEFT, with times from the call record
  • RECORDING_READY, with the bot's recording_id set to a calendar_recording on the same connection, and BOT_STATUS_CHANGE to DONE
  • the Teams transcript, one TRANSCRIPT_RECEIVED per transcript line

No data arrives while the meeting is running.

The same connection also gives you the meeting-related Microsoft Teams objects: calendar_calendar, calendar_event, calendar_busy, calendar_webinar, calendar_recording, uc_call and uc_recording. They work as on the Microsoft Teams integration (calendar_event and uc_call have native webhooks), and their scopes are requested only when you select their permissions.

For how to create bots and receive events, see How to Build a Meeting Bot with Unified.


Microsoft Teams (meeting bot) needs your own Microsoft Entra app. Unified has no shared credentials for this integration.

Call records need the Microsoft Graph application permission CallRecords.Read.All. It can't be requested during the user's OAuth sign-in. Unified gets an app-only token for it with your app's client ID and secret, against the user's tenant.

So the same Entra app your users authorize must also have CallRecords.Read.All (application) with admin consent in each customer's tenant. Only a Privileged Role Administrator or Global Administrator can grant it. Without it, creating the calendar_botevent webhook fails.

The delegated recording, transcript and AI insight permissions (OnlineMeetingRecording.Read.All, OnlineMeetingTranscript.Read.All, OnlineMeetingAiInsight.Read.All) need admin consent too. Users can consent on their own only to User.Read, OnlineMeetings.Read, Calendars.Read and the sign-in scopes.


What you'll need


Step 1: Register the Entra app

  1. In the Microsoft Entra admin center, go to Entra ID > App registrations > New registration. Under supported account types, choose Multiple Entra ID tenants: Unified signs users in through the common endpoint, so a single-tenant app fails for every user with AADSTS50194.
  2. Add a Web redirect URI for your Unified region:
    • US: https://api.unified.to/oauth/code
    • EU: https://api-eu.unified.to/oauth/code
    • AU: https://api-au.unified.to/oauth/code
  3. Create a client secret and copy its Value (not its Secret ID), with the Application (client) ID. Secrets expire after at most 24 months; replace the secret in Unified before it expires.

Step 2: Add Microsoft Graph permissions

Delegated permissions

PermissionUnified permissionUsed for
User.Read, openid, email, profile, offline_accessallSign-in and token refresh
OnlineMeetings.Readcalendar_bot_read, calendar_bot_write, calendar_botevent_read, calendar_recording_readFind the meeting by its join link; read recordings
Calendars.Readcalendar_bot_write, calendar_event_readCreate a bot from a calendar event_id; list events
OnlineMeetingRecording.Read.Allcalendar_botevent_read, calendar_recording_readRecording notifications and reads
OnlineMeetingTranscript.Read.Allcalendar_botevent_read, calendar_recording_readTranscript notifications and reads
OnlineMeetingAiInsight.Read.Allcalendar_recording_readReading the calendar_recording a bot points to (AI insights need a Microsoft 365 Copilot licence for each user)
If you select other permissions (calendars, busy, webinars, calls), add the same delegated permissions the Microsoft Teams integration uses for them.

Application permission

PermissionUsed for
CallRecords.Read.AllThe call-record subscription and reads. Needs admin consent.
Then select Grant admin consent (in your own tenant), and have each customer's tenant admin consent to the app in theirs: the application permission, and the delegated recording, transcript and AI insight permissions.

Step 3: Enter your credentials in Unified

  1. In the Unified app, open Integrations and activate Microsoft Teams (meeting bot).
  2. Enter your Entra app's client ID and client secret.
  3. Select the permissions you need: calendar_bot_read, calendar_bot_write and calendar_botevent_read for bots, calendar_event_read to list events (creating a bot from an event_id needs only calendar_bot_write), and calendar_recording_read to read recordings. Unified requests the matching delegated permissions above.

Step 4: Webhook setup

There is nothing to configure in Azure or Teams. When you create a native calendar_botevent webhook on a connection, Unified creates three Microsoft Graph subscriptions for it, and renews them automatically. (Graph limits these subscriptions to an hour; Unified creates them for 58 minutes.)

  1. communications/callRecords (app-only token): the call record
  2. the connected user's online meeting recordings (delegated)
  3. the connected user's online meeting transcripts (delegated)

Creating the webhook is all or nothing: if any of the three subscriptions can't be created, none is kept and the request fails. It returns 400 when the integration doesn't have your own Entra app's credentials, and fails when the tenant admin hasn't consented to CallRecords.Read.All.

Create the webhook before the meeting starts: Graph sends transcript notifications only when the subscription existed before transcription started. The bot must exist before the meeting ends: the call record is matched only to a bot that is still READY.


How end users connect

End users authorize Microsoft Teams (meeting bot) through Unified's authorization flow (embedded authorization or the authorization link) with their work or school account, and approve the delegated permissions. Their tenant admin must also consent to your app's CallRecords.Read.All application permission and to the delegated recording, transcript and AI insight permissions. A user who signs in before that sees AADSTS90094 (admin consent required).

App-only (client credentials) connections aren't supported: the bot looks up the meeting as the connected user.


Test the setup

  1. Create a calendar_bot with the Teams join link (meeting_url, a https://teams.microsoft.com/... link), or with the id of a calendar_event listed on the same connection (event_id; the event must have a Teams join link). The bot is READY.
  2. Create a native webhook for calendar_botevent / created on the same connection.
  3. Hold the meeting, with recording and transcription on.
  4. After the meeting, expect BOT_STATUS_CHANGE to CALL_ENDED and participant events, then RECORDING_READY, TRANSCRIPT_RECEIVED events and BOT_STATUS_CHANGE to DONE.
  5. Read the recording with GET /calendar/{connection_id}/recording/{recording_id} on the same connection.

Limits

  • Unsupported features return 501 (Not supported by this integration: …) on create and update: join_at, and any recording_config, even {}. The bot records nothing itself; recordings and transcripts come from Teams when the organizer turns them on. Malformed input, such as a link that isn't a Teams join link, returns 400.
  • After the meeting only. Microsoft delivers call records after the meeting: on average within 30 minutes, and up to 150 minutes. Recordings and transcripts can take up to 240 minutes.
  • Recordings and transcripts need the organizer. They are subscribed for scheduled meetings organized by the connected user. A bot on someone else's meeting gets the call record only.
  • Not covered by Graph recordings and transcripts: private-channel meetings, ad hoc calls, expired meetings, and meetings created through the API without a calendar event.
  • Tenant settings: a tenant admin can turn off Graph access to transcripts; reads then fail with 403 GraphAccessToTranscriptsDisabled.
  • The meeting must be visible to the connected user; otherwise creating the bot returns 404, as it does for a meeting or calendar event that doesn't exist. Creating another bot while an earlier bot for exactly the same link is still active (not CALL_ENDED, DONE or FATAL) returns 409.
  • Only name is updated. meeting_url and event_id in an update are ignored.
  • Large meetings: only the first page of call-record participants (130) and sessions (60) is read.
  • Several webhooks on one connection: call-record events go to whichever webhook's notification is processed first. Recording and transcript events go to every webhook.
  • Recurring meetings: recordings and transcripts attach to a bot only when created between the bot's created_at and its ended_at, so another occurrence doesn't attach.
  • A meeting with no recording stays in CALL_ENDED.
  • Deleting a bot deletes it; no event is sent.

Troubleshooting

❌ Creating a bot returns 400

  • The link isn't a https://teams.microsoft.com/... join link
  • The calendar event has no Teams join link

❌ Creating a bot returns 404

  • The meeting or calendar event doesn't exist
  • The connected user can't see the meeting

❌ Creating a bot returns 501

The request includes join_at or recording_config. Remove them.

❌ Creating the webhook fails

  • 400: the integration doesn't have your own Entra app's client ID and secret (Step 3)
  • The tenant admin hasn't consented to CallRecords.Read.All (Step 2)
  • AADSTS7000215: the client secret is wrong; enter the secret's Value, not its Secret ID
  • AADSTS7000222: the client secret has expired; create a new one and update it in Unified

❌ Users can't sign in

  • AADSTS50194: the app is single-tenant; change it to Multiple Entra ID tenants (Step 1)
  • AADSTS90094: the tenant admin hasn't granted admin consent (Step 2)

❌ The bot stays READY after the meeting

  • The bot or the calendar_botevent webhook was created after the meeting ended
  • Microsoft hasn't delivered the call record yet (up to 150 minutes)

❌ No recording or transcript events

  • The connected user didn't organize the meeting
  • Recording or transcription wasn't on in the meeting
  • The webhook was created after transcription started
  • The tenant has turned off Graph access to transcripts (403 GraphAccessToTranscriptsDisabled)
  • Microsoft hasn't delivered them yet (up to 240 minutes)
Share
All guidesAccounting Account Type Hierarchy in UnifiedAdvertising Report Metrics by IntegrationAmazon S3: Required IAM PermissionsATS to Vector DB: How to Power Talent Intelligence with Real-Time DataBuilding AI applications with Unified and LangbaseConcur & Concur (Company) — Connection GuideConfigure Greenhouse Native Candidate WebhooksConfigure Salesforce Meetings and Notes for UnifiedConnect Amazon Seller Central to Unified.toConnecting BambooHR via OAuth 2Connecting Google Workspace Integrations with a Service AccountCorrect WelcomeKit Scopes for Jungle IntegrationCreating Ads using the Unified Ads APICreating Walmart Items via UnifiedEnd-Users, Integrations, and ConnectionsEnriching Unified Objects with Custom MetadataEnterprise-managed authorization (EMA) with a static keyEnterprise-managed authorization (EMA) with a workspace secretEnterprise-managed authorization (EMA) with Microsoft Entra IDExtended observability — pushing API logs to ClickHouseExtended observability — pushing API logs to Grafana / LokiExtended Observability - Pushing API Logs to your Datadog InstanceFacebook Messenger Webhook SetupFireflies Integration GuideGetting started with WorkableHandling Delegated vs. Application Scopes in Microsoft IntegrationsHiBob & Unified: connection and time off guideHow long are logs retainedHow to access employees and usersHow to add API support for the Create Activity in CrelateHow to Associate a Connection ID with Your End-UserHow to build a Candidate Assessment product withHow to build a candidate sourcing or job board app with Unified.toHow to build a Discord support bot with Unified.to and LangbaseHow to Build a Fintech Application with Unified's Payments APIHow to Build an E-Commerce Product Integration with UnifiedHow to build an invoicing system with Unified.toHow to build Enterprise Search using RAGHow to configure Bullhorn Redirect URI for creating a connectionHow to configure webhooks in HubSpotHow to configure webhooks in Thrive LearningHow to configure webhooks in Toast for UnifiedHow to Connect Anthropic to Real-Time SaaS Data with Unified.to MCP ServerHow to Connect Cohere to Real-Time SaaS Data with Unified.to MCP ServerHow to Connect Google Gemini to Real-Time SaaS Data with Unified.to MCP ServerHow to connect HiBob (Bob) to Unified with OAuth 2.0How to Connect LLMs to Real-Time SaaS Data with Unified.to MCP ServerHow to Connect OpenAI to Real-Time SaaS Data with Unified.to MCP ServerHow to Connect Zoom and Zoom PhoneHow to create a Connection In Microsoft TeamsHow to create a token-based connection in HighlevelHow to Create a Unified.to Connection to HighLevelHow to Create a Xero Connection in Unified.toHow to create and configure webhooksHow to create Connection with HubspotHow to customize portal URLs for Stripe and GoCardlessHow to filter webhook eventsHow-to Get a Private Support Channel for Your Unified IntegrationHow to get help fromHow to get your 8x8 Connect API Key and Account ID: Step-by-step guideHow to get your ADP Workforce Now Oauth2 client ID, Oauth2 client secret, Oauth2 PEM certificate and Oauth2 private key: Step-by-step guideHow to get your Amazon S3 AWS Region, AWS S3 Key and AWS S3 Secret: Step-by-step guideHow to get your Ashby API Key: Step-by-step guideHow to get your Brex API token: Step-by-step guideHow to get your Bullhorn OAuth2 Client ID, OAuth2 Client Secret, API Username and API Password: Step-by-step guideHow to get your Ceridian Dayforce Username, Password and Client Namespace: Step-by-step guideHow to get your Crelate API key: Step-by-step guideHow to get your Dashlane API Key: Step-by-step guideHow to get your Deel Access Token: Step-by-step guideHow to get your Dialpad API Key: Step-by-step guideHow to get your Discord OAuth 2 credentials and bot tokenHow to get your ELMO client ID and client secret: Step-by-step guideHow to get your Gainsight Client ID, Client Secret and Gainsight API Domain: Step-by-step guideHow to get your Greenhouse API Key and Job board token: Step-by-step guideHow to get your Helpscout API Key: Step-by-step guideHow to get your HiBob Service User ID and Service User Token: Step-by-step guideHow to get your HighLevel API Key and Location ID: Step-by-step guideHow to get your HubSpot developer key and OAuth 2 credentials (Legacy Apps)How to get your Humaans API Key: Step-by-step guideHow to get your iCIMS API Username, API Password and Customer ID: Step-by-step guideHow to get your JobDiva Client ID, Username/email and Password: Step-by-step guideHow to get your Jobvite API Key, API Key Secret and Email: Step-by-step guideHow to get your Lever API Key: Step-by-step guideHow to get your Loxo Agency slug, API Key and Agency ID: Step-by-step guideHow to get your Microsoft Active Directory / Entra ID Client ID, Client Secret and Tenant ID: Step-by-step guideHow to get your Microsoft Azure AD OAuth 2 credentialsHow to get your OAuth 2 credentials for GmailHow to get your OAuth 2 credentials for Microsoft Dynamics 365How to get your OAuth 2 credentials in PipeDriveHow to get your OpenAI API Key: Step-by-step guideHow to get your Paycom SID/Username and Token/Password: Step-by-step guideHow to get your PCRecruiter Username, Password, Database ID, App Key and App ID: Step-by-step guideHow to get your SAP SuccessFactors (OpenID Connect) Username, Password, Client ID, Client Secret, Dependency Name, IAS Host and API URL: Step-by-step guideHow to get your SAP SuccessFactors Username, Company ID, Password and API URL: Step-by-step guideHow to get your Shopify Admin API access token and Store ID: Step-by-step guideHow to get your Wayfair Client ID, Client Secret and Vendor Encrypted Key: Step-by-step guideHow to get your Workable API Token and Subdomain: Step-by-step guideHow to get your yotpo APP Key / Store ID and Secret Key: Step-by-step guideHow to Handle Attachments in Invoice, Bill and Credit MemoHow to migrate or import your integrations into Unified.toHow to obtain your GitHub OAuth2 credentialsHow to Power Claude with Live Customer Data Using Unified MCPHow to receive assessment orders from an ATS with Unified.toHow to register a Google developer app and get OAuth 2 credentialsHow to register a Salesforce developer app and get OAuth 2 credentialsHow to register a Slack developer account and get OAuth 2 credentialsHow to Register a Workday Developer App and Get OAUTH2 CredentialsHow to Register a Workday Developer App and Get OAUTH2/ SOAP CredentialsHow to Register a ZendeskSell Developer App and Get OAUTH2 CredentialsHow to Register a Zoho Developer App and Get OAUTH2 CredentialsHow to Register an ADP Developer App and Get OAUTH2 CredentialsHow to Register an Atlassian Developer App and Get OAUTH2 CredentialsHow to Register your Facebook OAuth2 ApplicationHow to Register your Google Ads OAuth2 ApplicationHow to Register your MetaAds OAuth2 applicationHow To Request & Write Raw Integration DataHow to Retrieve Microsoft Dynamics 365 Business Central CredentialsHow to set up a custom API URL with a CNAMEHow to Set Up a Google Meet Meeting Bot with UnifiedHow to set up a HiBob marketplace installHow to Set Up a Microsoft Teams Bot with UnifiedHow to Set Up a Webex Meeting Bot with UnifiedHow to Set Up a Zoom Meeting Bot with UnifiedHow to set up and configure NotionHow to set up Google Business Profile API access and get OAuth 2 credentialsHow to Set Up LinkedIn Webhooks with UnifiedHow to set up native webhooks for QuickBooks Online with UnifiedHow to set up Slack webhooks using event subscriptionsHow to set up Telegram (bot) with Unified.toHow to set up your scopes in HubSpotHow to setup a Freshbooks developer appHow to setup a TikTok Shop applicationHow to Setup AWS Assume Role for AWS Secret ManagerHow to setup Oracle JD EdwardsHow to Setup Quickbooks DesktopHow to setup Sage 100 with Unified.toHow to test webhooks in the Synthetic Data (Sandbox) environmentHow to troubleshoot unhealthy connectionsHow to troubleshoot unhealthy webhooksHow to use SCIM with the Unified APIHow to use the Passthrough APIHow to Use Unified.to's Generative AI API with OpenAI and ClaudeHow to use User Provisioning and Verification with UnifiedHow virtual webhook retry and recovery worksIntegration set-up guide for LeverManaging custom validation rules in SalesforceNetSuite Authentication Setup for UnifiedNotice of Deprecation of Fields and Objects (Nov 2025)Oracle HCM / Taleo OAuth Credentials GuideRetrieval-Augmented Generation (RAG)SalesForce (External Client Apps) on Multiple OrganizationsSAML Single-Sign-OnScaling MCP Tools with Anthropic's (& OpenAI's) Defer LoadingSet Environments for your Unified.to WorkspaceSetting up a Slack Bot Connection with Unified.toSetting up OAuth 2 Credentials for Greenhouse's APIsTrello Connection Guide in UnifiedUnderstanding OAuth2 Authorization flowsUnderstanding Response Time Headers in the Unified APIUnified Assessment API: Embed Assessments Inside ATS PlatformsUnified's MCP ServerUnified MCP Server with LangChain and LangGraphUnlock real-time data with virtual webhooksUse Unified to sign in your users into your applicationVincere OAuth Setup Guide for UnifiedWorkable: Creating candidates and attaching them to jobsWorkday vs Workday Legacy — Why Unified.to Supports BothWorking with hierarchical tree data in Storage, Messaging, and KMS APIs
Are we missing anything? Let us know
Was this page helpful?