Skip to navigation

Microsoft Teams Bots

How MeetStream bots join, wait, record and leave Microsoft Teams meetings
View as Markdown

Send a bot into any Microsoft Teams meeting with one API call, then use this page to predict exactly what it will do at every step.

A guest bot needs no setup. Pass a Teams join link to create_bot and the bot joins as a guest:

curl -X POST "https://api.meetstream.ai/api/v1/bots/create_bot" \
-H "Authorization: Token <YOUR_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"meeting_link": "https://teams.microsoft.com/l/meetup-join/...",
"bot_name": "Notetaker",
"video_required": false
}'

Teams at a glance

BehaviorOn Microsoft Teams
SetupNone for guest bots. Signed-in bots need a Microsoft 365 bot account.
Accepted hoststeams.microsoft.com, teams.live.com, *.teams.microsoft.us (details)
IdentityAnonymous guest (Teams shows Unverified next to the name), or a signed-in Microsoft 365 account
Lobby timeoutwaiting_room_timeout, default 1200 s, range 60 to 1800
Everyone-left timeouteveryone_left_timeout, default 1200 s, range 60 to 1800
Recording startsOn the first audio frame after the bot is admitted. There is no host permission prompt.
Mixed audioAlways recorded, 48 kHz
Per-participant audioSplit out of the Teams mixed stream by speaker, up to 16 participants
Per-participant videoUp to 6 streams at once (at most 1 screen share), up to 1280x720 at 15 fps
ChatRead and send
Native captionsCollected only when the meeting is already producing captions
Meeting scopeRegular Teams meetings. The bot stays in the main meeting.

Every status and error reason on this page is also listed in the Bot status and error code reference. Webhook payloads are described in Webhooks and events.

MeetStream checks the scheme and hostname strictly and the path loosely. The bot opens your link unchanged, including any ?p= passcode.

LinkAcceptedNotes
https://teams.microsoft.com/l/meetup-join/...YesThe classic join link from Outlook and Teams invites.
https://teams.microsoft.com/meet/<meeting-id>?p=<passcode>YesShort join links. Keep the p passcode parameter.
https://teams.live.com/meet/<meeting-id>?p=<passcode>Yes, guest onlyPersonal Teams (Teams free). Signed-in bots are rejected, see below.
https://<name>.teams.microsoft.us/...YesGovernment cloud hosts. The bot uses the same guest join flow.
https://teams.cloud.microsoft/...NoThis is the address of the Teams web app, not a meeting link. Copy the join link from the meeting invite instead.
A Teams link whose path is empty, such as https://teams.microsoft.com/#/l/... or https://teams.microsoft.com/?meetingjoin=...NoTreated as the Teams home page. Use the /l/meetup-join/... or /meet/... form of the link.
Teams pages such as /signin, /login, /download, /helpNoNot meeting links.

A rejected link returns 400 before any bot is created. For a host that is not a meeting platform the message starts with meeting_link host '<host>' is not a supported meeting platform. For a home or marketing page it reads meeting_link looks like the Teams site root or a marketing page, not a meeting URL: '<link>'.

Outlook Calendar auto-join recognizes links on teams.microsoft.com and teams.live.com. For government cloud links (*.teams.microsoft.us), create bots with create_bot.

Work or school Teams vs personal Teams

Meeting typeLink hostGuest botSigned-in bot
Microsoft 365 (work or school)teams.microsoft.comYesYes
Personal Teams (Teams free)teams.live.comYesUse a guest bot. A signed-in request returns 400 with teams.login_required is not supported for personal Teams (teams.live.com) links.

Guest or signed-in: which bot to send

A guest bot types its bot_name on the Teams pre-join screen and joins as an anonymous participant. A signed-in bot first signs in to a Microsoft 365 bot account that you register with MeetStream, then joins as that account. Set it up once with the Teams signed-in bots guide.

Your situationGuest botSigned-in bot
Organizer lets everyone bypass the lobbyJoins directlyJoins directly
Lobby is on and someone will admit the botWaits, then joins when admittedOften bypasses the lobby (matrix)
Organization has turned anonymous join offCannot joinJoins, as long as Teams does not treat the bot’s organization as anonymous (details)
Organizer requires unverified participants to verify by emailCannot joinJoins
Personal Teams (teams.live.com) linkJoinsUse a guest bot
You want the name in the meeting to be your bot_nameYesNo, Teams shows the Microsoft account’s name
Many bots at the same timeNot limited by accountsOne bot per bot account at a time

Display name, camera and microphone

Display name. For Teams links, bot_name may contain only letters, numbers, spaces, hyphens (-), apostrophes ('), periods (.), underscores (_) and at signs (@). This is the pattern ^[A-Za-z0-9 \-'\._@]+$, checked after leading and trailing spaces are trimmed. Anything else returns 400: bot_name contains unsupported special characters. .... If you omit bot_name, the bot is called MeetStream.

  • Guest bots show your bot_name. Teams adds its own Unverified label next to anonymous participants.
  • Signed-in bots always show the Microsoft account’s display name. bot_name is not typed anywhere, so set the name (and profile picture) on the account itself.

Camera. The bot joins with its camera off, unless you set bot_image_url. With an image, the bot shows that image as its video, for guest and signed-in bots alike. You can change it during the call with send_image.

Microphone. The bot joins muted and stays muted unless it plays audio into the meeting, for example as a MIA voice agent or through the control WebSocket.

How a Teams bot joins

No Yes Yes Sign-in fails Signed in No Meeting not found No permission to join Sign-in wall Nothing after 3 tries Pre-join screen Admitted or bypassed the lobby Host declines waiting_room_timeout passes POST create_bot Link and fields valid? 400, no bot created Status: Joining teams.login_required and domainlogin mode is always? Sign in to the Microsoft 365 bot account Error, ErrorReason TeamsLogin... Open the meeting link What does Teams show? Failed, InvalidMeetingLink Failed, MeetingRestricted Error, TeamsSessionNotApplied Error, JoinButtonTimeout Set name, camera off or botimage, mic muted, click Join Status: InWaitingRoom Admitted? Status: InMeeting, then Recording Denied, HostDenied NotAllowed, WaitingRoomTimeout

Scheduled bots. A bot created with join_at starts about a minute before that time and clicks Join no earlier than 30 seconds before it, so it is usually in the lobby when the meeting starts.

What counts as “admitted”. The bot moves to InMeeting when the in-meeting controls appear, or when Teams lists the bot in the call roster (not the lobby). The roster check matters for meetings where the organizer has hidden the chat.

The Teams lobby

What the bot reports while it waits

  • InWaitingRoom (webhook bot.in_waiting_room) fires when the bot clicks Join, not when Teams actually holds it in the lobby. A bot that bypasses the lobby still passes through InWaitingRoom for a moment, then moves to InMeeting. Do not treat bot.in_waiting_room alone as proof that someone must admit the bot.
  • The lobby timer is automatic_leave.waiting_room_timeout. On Teams the default is 1200 seconds (20 minutes) and you can set 60 to 1800. It starts at the Join click. If nobody admits the bot in time, the bot leaves with status NotAllowed, ErrorReason WaitingRoomTimeout (or JoinHang if the join never settled), and the message Failed: Bot not allowed to join meeting.
  • If the meeting has not started, Teams by default keeps anonymous participants in the lobby until a verified (signed-in) participant joins. On Teams, the bot waits in the lobby until it is admitted or waiting_room_timeout passes.
{
"meeting_link": "https://teams.microsoft.com/l/meetup-join/...",
"bot_name": "Notetaker",
"automatic_leave": { "waiting_room_timeout": 1800 }
}

Who gets past the lobby

The organizer controls this in Meeting options > Who can bypass the lobby. Admins set the default in the Teams admin center under Meetings > Meeting policies > Meeting join & lobby > Who can bypass the lobby, and organizers can change it per meeting (Microsoft Learn).

”Who can bypass the lobby”Guest botSigned-in bot, account in the organizer’s organizationSigned-in bot, account in another organization
EveryoneBypassesBypassesBypasses
People in my org, trusted orgs, and guestsWaits in lobbyBypassesBypasses, if the two organizations trust each other in external access
People in my org and guestsWaits in lobbyBypassesWaits in lobby
People in my orgWaits in lobbyBypassesWaits in lobby
People who were invitedWaits in lobbyBypasses if the bot account was sent or forwarded the inviteBypasses if the bot account was sent or forwarded the invite
Only organizers and co-organizersWaits in lobbyWaits in lobbyWaits in lobby

Good to know:

  • A signed-in bot signs in to its own organization. When that organization is not the organizer’s, Teams treats the bot as a person from an external organization, and only if both organizations allow each other in external access (Teams admin center Users > External access). Otherwise Teams treats it as anonymous, exactly like a guest bot. The Microsoft default is Allow all external domains, and Microsoft blocks external access with trial-only tenants by default, so use a paid Microsoft 365 tenant for bot accounts (Microsoft Learn).
  • To get a signed-in bot through People who were invited, add the bot account’s email address to the meeting invite.
  • Whoever is allowed to admit people (by default, organizers and presenters) sees the bot’s join request in the lobby and can admit or decline it.

Organizations that block anonymous join

Teams admins can stop anonymous people, and therefore guest bots, from joining meetings with any of these settings (Microsoft Learn):

SettingWhere
Anonymous users can join a meeting (whole organization)Teams admin center > Meetings > Meeting settings > Participants
Anonymous users can join a meeting unverified (per organizer)Teams admin center > Meetings > Meeting policies > Meeting join & lobby
Require unverified participants to verify their info before joining (per meeting)The organizer’s Meeting options

These meetings admit only signed-in or verified participants. Send a signed-in bot. A guest bot reports one of these results:

  • Usually status Error with ErrorReason TeamsSessionNotApplied and the message Error: Teams signed-in session was not applied to the meeting. This happens even for guest bots that never tried to sign in: it means Teams showed a sign-in wall.
  • Depending on the exact screen Teams shows, you may instead get Failed / MeetingRestricted (a “no permission” page) or Error / JoinButtonTimeout (the pre-join screen never appeared).

The fix is to send a signed-in bot, ideally with an account in the organizer’s organization or in an organization it trusts.

When the bot can’t get in, or gets removed

Final statuses arrive as a bot.stopped webhook whose bot_event names the cause, with status_code 500 for failures and 200 otherwise. The ErrorReason is returned as bot_details.ErrorReason by GET /api/v1/bots/{bot_id}/detail.

What happenedStatusErrorReasonMessagebot_event
Nobody admitted the bot before waiting_room_timeoutNotAllowedWaitingRoomTimeout or JoinHangFailed: Bot not allowed to join meetingbot.notallowed
Someone declined the bot in the lobbyDeniedHostDeniedFailed: Denied to join meetingbot.denied
Someone removed the bot from the meetingKicked (sent as bot_status: "Stopped")KickedByHostBot was removed from the meeting by a participantbot.kicked
Teams says the bot has no permission to joinFailedMeetingRestrictedFailed: Meeting access is restrictedbot.failed
Teams can’t find the meeting (wrong link, ID or passcode), even after the bot reached the lobbyFailedInvalidMeetingLinkFailed: Invalid meeting linkbot.failed
Teams shows a sign-in wallErrorTeamsSessionNotAppliedError: Teams signed-in session was not applied to the meetingbot.failed
Teams shows “We couldn’t authenticate you” or “app failed to init”ErrorTeamsSessionAuthFailedError: Teams browser session authentication failedbot.failed
The pre-join screen never loadedErrorJoinButtonTimeoutError: Join on web timeout after 3 attemptsbot.failed
A signed-in bot’s Teams app kept failing after JoinErrorTeamsAppInitFailedbot.failed
The signed-in bot could not sign inErrorTeamsLogin..., see sign-in errorsError: Teams sign-in failed: ...bot.failed

Other Error and Failed reasons, for example JoinFlowException or JoinRetryExhausted, do not come from meeting settings. Create the bot again. If the reason occurs again, contact support with the bot ID. See the status and error code reference.

When the meeting ends

What happenedStatusMessage
The host ended the meeting for everyoneStoppedBot exited the call: Meeting was ended by the host
The call ended without an end screenStoppedSame as above, reported after about 15 seconds without call controls
Everyone else leftStoppedBot exited the call: Everyone left the meeting
No sound for voice_inactivity_timeoutStoppedBot exited the call: Voice inactivity timeout
You called remove_botStoppedBot stopped via API request

The bot normally reports Leaving (bot.leaving) just before the final status. After a bot that was in the meeting stops, post-call processing runs as usual.

What MeetStream retries for you

SituationRetry
The Teams pre-join screen is slow to loadUp to 3 attempts of 40 seconds each, then JoinButtonTimeout
A signed-in bot’s Teams app fails right after JoinUp to 2 more tries in the same signed-in session, then TeamsAppInitFailed
The bot session does not start before the bot clicks JoinMeetStream starts the join again, up to 2 times
Wrong password for a signed-in botNever retried, to avoid locking the account
Denied, removed, lobby timeout, invalid link, restricted meetingNot retried: another attempt would hit the same result. Fix the cause and create a new bot.

Automatic leave on Teams

SettingTeams defaultRangeHow it behaves on Teams
waiting_room_timeout120060 to 1800Starts when the bot clicks Join. Ends in NotAllowed.
everyone_left_timeout120060 to 1800Starts once the bot is in the meeting and sees no other participants (checked every 5 seconds). When the timer ends the bot checks again and leaves only if it is still alone. This also covers a meeting that is empty when the bot is admitted.
voice_inactivity_timeout600 when you don’t set it60 to 1800Starts when the bot is admitted and restarts on any non-silent audio from the meeting, not only speech, so background noise from an unmuted microphone keeps the bot in.
no_one_joined_timeout1200 (accepted)60 to 1800On Teams, everyone_left_timeout also ends a session that nobody joins.
in_call_recording_timeout14400 (accepted)600 to 18000On Teams, set the maximum length with everyone_left_timeout, voice_inactivity_timeout or a remove_bot call.
recording_permission_denied_timeoutZoom only. Ignored on Teams.
bot_detection.using_participant_namesOffSee auto-leaveWorks on Teams. After activate_after seconds in the meeting, if every other participant’s name contains one of your matches, the bot leaves after timeout seconds.

Signed-in bots in one-to-one meetings. A signed-in bot starts the everyone-left timer when it sees one or fewer other participants in the Teams roster. For one-to-one calls, set everyone_left_timeout to 1800, or send a guest bot.

Other Teams notes:

  • Bot detection matches substrings, case-insensitively. Teams adds labels such as Unverified next to anonymous people, so match on distinctive words from the other bot’s name (for example notetaker).
  • A bot that leaves because of bot detection sends bot.leaving with a message that mentions bot_detection.using_participant_names, then bot.stopped.

Full reference for every timeout: Automatic leave configuration.

What the bot captures on Teams

Audio

  • Mixed audio is always recorded at 48 kHz. Recording (bot.recording) fires on the first audio frame after the bot is admitted.
  • Per-participant audio (audio_separate_streams: true): Teams mixes everyone’s audio on its servers and sends the bot a single stream, tagged with who is contributing to each moment of sound. MeetStream uses those tags to split the stream into a file per speaker, for up to 16 participants. When several people talk at once, that stretch of audio goes to one of them. For overlapping speech, use the mixed recording with a diarizing provider. See per-participant audio.
  • Real-time audio over the audio WebSocket labels mixed frames with the current dominant speaker’s display name. With per-participant audio on, per-participant frames replace the mixed ones once they start flowing.

Video

  • Per-participant video (video_separate_streams: true): the bot records up to 6 video streams at once, including at most 1 screen share. When all 6 slots are taken, a new screen share replaces the most recently added webcam, and additional webcams wait for a free slot. Each stream is recorded at up to 1280x720 and 15 fps (about 1.5 Mbps for webcams and 2.5 Mbps for screen share). Teams chooses the resolution it sends and can change it during the call; each recorded stream keeps one output size for its whole life. See per-participant video.
  • Meeting video (video_required: true) is built after the call in the layout set by recording_config.video_layout (grid_view, the default, or speaker_view). Per-participant video files require video_separate_streams: true explicitly. The recording is ready once post-call processing finishes.
  • Screen share is recorded as its own stream and logged as screenshare_on / screenshare_off participant events.

Chat and visuals

  • Reading chat: the bot records the meeting chat messages Teams delivers to it while it is in the meeting, without posting anything itself. Fetch them with get_chats or receive participant_events.chat_message webhooks. Teams meeting chat is a group conversation, so every captured message is recorded as sent to everyone.
  • Sending chat: bot_message posts once when the bot is admitted (only if it is not empty), and send_message posts during the call. Guest and signed-in bots both post into the meeting chat. If the meeting’s chat is turned off, there is nothing for the bot to read or post to.
  • Images: bot_image_url and send_image show an image, including animated GIFs, as the bot’s video.
  • Recording controls: pause and resume work on Teams while the bot is in the meeting.

Participants and speakers

  • Participant events: join, leave, webcam_on / webcam_off, screenshare_on / screenshare_off, chat_message, and speech_on / speech_off. On Teams, speech_on and speech_off follow each person’s microphone mute state, not detected speech.
  • The host flag is set for the meeting organizer.
  • People waiting in the lobby are not reported as joined until they are admitted.
  • The speaker timeline is built from Teams’ audio contributor tags together with audio level.

Transcription and captions

  • Post-call transcription works with every post-call provider.
  • Live transcription on Teams supports deepgram_streaming, assemblyai_streaming, sarvam_streaming and meetstream_streaming. If you list more than one, the first in that order is used. The bot sends the mixed audio to the provider, and each result is labelled with the dominant speaker. Set live_transcription_required.webhook_url to receive results. See live transcription.
  • Teams captions (meeting_captions provider): the bot collects the captions that Teams produces while someone in the meeting has live captions or transcription running. Ask a participant to turn on live captions, so that the caption file has content. Every caption is labelled English (en) whatever language is spoken. Captions are saved to the caption file after the call, not streamed live, and captions spoken while recording is paused are skipped. See Meeting Captions.

Meeting scope on Teams

  • The bot joins regular Teams meetings and stays in the main meeting. Host webinars, town halls and live events as regular Teams meetings for the bot to join.
  • Signed-in bots use commercial Microsoft 365 accounts. For government cloud meetings, send a guest bot.

Find your Teams bots in the dashboard

Open All Bots, set the platform filter to Teams, and filter by status to find bots that did not get in.

All Bots page in the MeetStream dashboard with the platform filter, the status filter and the Status column highlighted
All Bots: (1) filter by platform, (2) filter by status, (3) read each bot's status. Hover a badge to see the raw API status.

Signed-in bot domains and accounts live under Integrations > Teams Bots:

Integrations page in the MeetStream dashboard with the Teams Bots tab selected, showing No Teams domains yet and the Add domain button
Integrations > Teams Bots: (1) Integrations, (2) the Teams Bots tab, (3) Add domain.

Outlook Calendar auto-join

Teams meetings usually live on Outlook calendars. Connect Outlook with POST /api/v1/calendar/create_outlook_calendar and an Azure app registration, and MeetStream syncs events, detects Teams links and schedules bots for them. Full walkthrough: Outlook Calendar Setup. Scheduling patterns: Scheduling bots.

Microsoft Teams FAQ

The meeting’s Who can bypass the lobby setting doesn’t let the bot in, and nobody has admitted it yet. Ask someone to admit it, raise waiting_room_timeout (up to 1800 on Teams), or send a signed-in bot that the setting lets through (matrix). If the meeting hasn’t started, Teams by default holds anonymous participants until a verified (signed-in) participant joins.

On Teams, InWaitingRoom is reported the moment the bot clicks Join, before Teams decides whether to hold it. A bot that bypasses the lobby moves on to InMeeting right after. Use bot.inmeeting as your “joined” signal.

Teams showed a sign-in wall: the organizer’s organization doesn’t allow anonymous participants in this meeting. The reason name mentions a signed-in session, but it is also reported for guest bots. Send a signed-in bot instead. See Organizations that block anonymous join.

Teams bot names may only contain letters, numbers, spaces and the characters -, ', ., _ and @. Remove emoji, brackets, colons and other punctuation.

A signed-in bot joins as its Microsoft 365 account, so Teams shows the account’s display name. Change the display name on the account in the Microsoft 365 admin center.

Check the final message. Voice inactivity timeout means the meeting was silent for voice_inactivity_timeout seconds (600 by default). Everyone left the meeting means the bot saw nobody else when its timer ended; for signed-in bots in one-to-one meetings, see the note in Automatic leave on Teams. A bot.leaving message that mentions bot_detection means your bot detection matched every remaining name.

On Teams, set the maximum length with everyone_left_timeout, voice_inactivity_timeout or a remove_bot call when you’re done.

The bot only collects captions Teams is already producing. Teams produces them while someone in the meeting has live captions or transcription turned on. For a transcript that doesn’t depend on that, use a post-call or live transcription provider.

Teams sends one mixed audio stream. MeetStream splits it by the speaker tags Teams attaches, and when people talk over each other that stretch is attributed to one of them. Use the mixed recording with a diarizing provider when overlaps matter.

The bot records up to 6 video streams at a time, including at most one screen share. When the slots are full, extra webcams are not recorded until a slot frees up, and a new screen share takes the slot of the most recently added webcam.

Regular Teams meetings. The bot stays in the main meeting during breakout rooms. Host webinars, town halls and live events as regular Teams meetings for the bot to join.

Next steps