Skip to main content

Microsoft Teams Setup

Connect VibeOS to Microsoft Teams as a bot. Unlike Slack's Socket Mode, Teams delivers messages by calling a public HTTPS webhook, so your instance needs a publicly reachable endpoint — either a dev tunnel (local dev) or a real domain (production).

Need meeting summaries from Microsoft Graph events rather than normal bot conversations? Use the dedicated setup page: Teams Meetings.

Run vibeos gateway setup and pick Microsoft Teams for a guided walk-through.

How the Bot Responds​

ContextBehavior
Personal chat (DM)Bot responds to every message. No @mention needed.
Group chatBot only responds when @mentioned.
ChannelBot only responds when @mentioned.

Teams delivers @mentions as regular messages with <at>BotName</at> tags, which VibeOS strips automatically before processing.


For source or local installs, include the Teams extra so the bundled adapter can import the Microsoft Teams SDK:

uv sync --extra teams
# or, for editable installs:
uv pip install -e ".[teams]"

Step 1: Install the Teams CLI​

The @microsoft/teams.cli automates bot registration — no Azure portal needed.

npm install -g @microsoft/teams.cli@preview
teams login

To verify your login and find your own AAD object ID (needed for TEAMS_ALLOWED_USERS):

teams status --verbose

Step 2: Expose the Webhook Port​

Teams cannot deliver messages to localhost. For local development, use any tunnel tool to get a public HTTPS URL. The default port is 3978 — change it with TEAMS_PORT if needed.

# devtunnel (Microsoft)
devtunnel create vibeos-bot --allow-anonymous
devtunnel port create vibeos-bot -p 3978 --protocol https # replace 3978 with TEAMS_PORT if changed
devtunnel host vibeos-bot

# ngrok
ngrok http 3978 # replace 3978 with TEAMS_PORT if changed

# cloudflared
cloudflared tunnel --url http://localhost:3978 # replace 3978 with TEAMS_PORT if changed

Copy the https:// URL from the output — you'll use it in the next step. Leave the tunnel running while developing.

For production, point your bot's endpoint at your server's public domain instead (see Production Deployment).


Step 3: Create the Bot​

teams app create \
--name "VibeOS" \
--endpoint "https://<your-tunnel-url>/api/messages"

The CLI outputs your CLIENT_ID, CLIENT_SECRET, and TENANT_ID, plus an install link for Step 6. Save the client secret — it won't be shown again.


Step 4: Configure Environment Variables​

Add to ~/.vibeos/.env:

# Required
TEAMS_CLIENT_ID=<your-client-id>
TEAMS_CLIENT_SECRET=<your-client-secret>
TEAMS_TENANT_ID=<your-tenant-id>

# Restrict access to specific users (recommended)
# Use AAD object IDs from `teams status --verbose`
TEAMS_ALLOWED_USERS=<your-aad-object-id>

Step 5: Start the Gateway​

VIBEOS_UID=$(id -u) VIBEOS_GID=$(id -g) docker compose up -d gateway

This starts the gateway. The default webhook port is 3978 (override with TEAMS_PORT). Check that it's running:

curl http://localhost:3978/health   # should return: ok
docker logs -f vibeos

Look for:

[teams] Webhook server listening on 0.0.0.0:3978/api/messages

Step 6: Install the App in Teams​

teams app get <teamsAppId> --install-link

Open the printed link in your browser — it opens directly in the Teams client. After installing, send a direct message to your bot — it's ready.


Configuration Reference​

Environment Variables​

VariableDescription
TEAMS_CLIENT_IDAzure AD App (client) ID
TEAMS_CLIENT_SECRETAzure AD client secret
TEAMS_TENANT_IDAzure AD tenant ID
TEAMS_ALLOWED_USERSComma-separated AAD object IDs allowed to use the bot
TEAMS_ALLOW_ALL_USERSSet true to skip the allowlist and allow anyone
TEAMS_HOME_CHANNELConversation ID for cron/proactive message delivery
TEAMS_HOME_CHANNEL_NAMEDisplay name for the home channel
TEAMS_PORTWebhook port (default: 3978)

config.yaml​

Alternatively, configure via ~/.vibeos/config.yaml:

platforms:
teams:
enabled: true
extra:
client_id: "your-client-id"
client_secret: "your-secret"
tenant_id: "your-tenant-id"
port: 3978

Features​

Interactive Approval Cards​

When the agent needs to run a potentially dangerous command, it sends an Adaptive Card with four buttons instead of asking you to type /approve:

  • Allow Once — approve this specific command
  • Allow Session — approve this pattern for the rest of the session
  • Always Allow — permanently approve this pattern
  • Deny — reject the command

Clicking a button resolves the approval inline and replaces the card with the decision.

Meeting Summary Delivery (Teams Meeting Pipeline)​

When the Teams meeting pipeline plugin is enabled, this adapter also handles outbound delivery of meeting summaries — one Teams integration surface, not two. After a meeting's transcript is summarized, the writer posts the summary into your chosen Teams target.

Pipeline summary delivery is configured under the teams platform entry alongside the bot config:

platforms:
teams:
enabled: true
extra:
# existing bot config (client_id, client_secret, tenant_id, port) ...

# Meeting summary delivery (only used when the teams_pipeline plugin is enabled)
delivery_mode: "graph" # or "incoming_webhook"
# For delivery_mode: graph — pick ONE of:
chat_id: "19:meeting_..." # post into a Teams chat
# team_id: "..." # OR post into a channel
# channel_id: "..."
# access_token: "..." # optional; falls back to MSGRAPH_* app credentials
# For delivery_mode: incoming_webhook:
# incoming_webhook_url: "https://outlook.office.com/webhook/..."
ModeUse whenTrade-off
incoming_webhookSimple "post a summary into this channel" with a static Teams-generated URL.No reply threading, no reactions, shows as the webhook's configured identity.
graphThreaded channel posts or 1:1/group chat posts under the bot's identity via Microsoft Graph.Requires the Graph app registration with ChannelMessage.Send (channel) or Chat.ReadWrite.All (chat) application permissions.

If the teams_pipeline plugin is not enabled, these settings are inert — they only wire up when the pipeline runtime binds to the Graph webhook ingress.


Production Deployment​

For a permanent server, skip devtunnel and register your bot with your server's public HTTPS endpoint:

teams app create \
--name "VibeOS" \
--endpoint "https://your-domain.com/api/messages"

If you've already created the bot and just need to update the endpoint:

teams app update --id <teamsAppId> --endpoint "https://your-domain.com/api/messages"

Make sure your configured port (TEAMS_PORT, default 3978) is reachable from the internet and that your TLS certificate is valid — Teams rejects self-signed certificates.


Troubleshooting​

ProblemSolution
health endpoint works but bot doesn't respondCheck that your tunnel is still running and the bot's messaging endpoint matches the tunnel URL
KeyError: 'teams' in logsRestart the container — this is fixed in the current version
Bot responds with auth errorsVerify TEAMS_CLIENT_ID, TEAMS_CLIENT_SECRET, and TEAMS_TENANT_ID are all set correctly
No inference provider configuredCheck that ANTHROPIC_API_KEY (or another provider key) is set in ~/.vibeos/.env
Bot receives messages but ignores themYour AAD object ID may not be in TEAMS_ALLOWED_USERS. Run teams status --verbose to find it
Tunnel URL changes on restartdevtunnel URLs are persistent if you use a named tunnel (devtunnel create vibeos-bot). ngrok and cloudflared generate a new URL each run unless you have a paid plan — update the bot endpoint with teams app update when it changes
Teams shows "This bot is not responding"The webhook returned an error. Check docker logs vibeos for tracebacks
[teams] Failed to connect in logsThe SDK failed to authenticate. Double-check your credentials and that the tenant ID matches the account you used in teams login

Security​

warning

Always set TEAMS_ALLOWED_USERS with the AAD object IDs of authorized users. Without this, anyone who can find or install your bot can interact with it.

Treat TEAMS_CLIENT_SECRET like a password — rotate it periodically via the Azure portal or Teams CLI.

  • Store credentials in ~/.vibeos/.env with permissions 600 (chmod 600 ~/.vibeos/.env)
  • The bot only accepts messages from users in TEAMS_ALLOWED_USERS; unauthorized messages are silently dropped
  • Your public endpoint (/api/messages) is authenticated by the Teams Bot Framework — requests without valid JWTs are rejected