Microsoft Teams Bots
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:
Teams at a glance
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.
Supported meeting links
MeetStream checks the scheme and hostname strictly and the path loosely. The bot opens your link unchanged, including any ?p= passcode.
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
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.
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_nameis 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
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(webhookbot.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 throughInWaitingRoomfor a moment, then moves toInMeeting. Do not treatbot.in_waiting_roomalone 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 statusNotAllowed, ErrorReasonWaitingRoomTimeout(orJoinHangif the join never settled), and the messageFailed: 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_timeoutpasses.
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).
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):
These meetings admit only signed-in or verified participants. Send a signed-in bot. A guest bot reports one of these results:
- Usually status
Errorwith ErrorReasonTeamsSessionNotAppliedand the messageError: 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) orError/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.
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
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
Automatic leave on Teams
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.leavingwith a message that mentionsbot_detection.using_participant_names, thenbot.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 byrecording_config.video_layout(grid_view, the default, orspeaker_view). Per-participant video files requirevideo_separate_streams: trueexplicitly. The recording is ready once post-call processing finishes. - Screen share is recorded as its own stream and logged as
screenshare_on/screenshare_offparticipant 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_chatsor receiveparticipant_events.chat_messagewebhooks. Teams meeting chat is a group conversation, so every captured message is recorded as sent toeveryone. - Sending chat:
bot_messageposts once when the bot is admitted (only if it is not empty), andsend_messageposts 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_urlandsend_imageshow 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, andspeech_on/speech_off. On Teams,speech_onandspeech_offfollow 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_streamingandmeetstream_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. Setlive_transcription_required.webhook_urlto receive results. See live transcription. - Teams captions (
meeting_captionsprovider): 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.

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

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
Why is my Teams bot stuck in the waiting room?
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.
Why did I get bot.in_waiting_room when the bot was let straight in?
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.
Why did my guest bot fail with TeamsSessionNotApplied?
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.
Why is my Teams link rejected with a 400?
Check the host and path against Supported meeting links. The usual causes are a teams.cloud.microsoft address copied from the browser, or a link with an empty path such as https://teams.microsoft.com/#/l/.... Copy the join link from the meeting invite instead.
Why does bot_name return a 400 on Teams?
Teams bot names may only contain letters, numbers, spaces and the characters -, ', ., _ and @. Remove emoji, brackets, colons and other punctuation.
Why does my signed-in bot show a different name than bot_name?
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.
Why did the bot leave while people were still in the meeting?
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.
How do I set a maximum meeting length on Teams?
On Teams, set the maximum length with everyone_left_timeout, voice_inactivity_timeout or a remove_bot call when you’re done.
Why is my Teams caption file empty?
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.
Why do two speakers end up in one per-participant audio file?
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.
Why are only some participants' videos recorded?
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.
Which Teams meeting types does the bot join?
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
- Set up Microsoft Teams signed-in bots for meetings that block anonymous guests
- Bot status and error code reference
- Automatic leave configuration
- Webhooks and events
- Debugging bots
