Microsoft Teams configuration
Microsoft Teams configuration keys, environment variables, and history limits
The channels.msteams settings, the environment variables that stand in for the auth keys, and the history context rules.
Multiple bot accounts
Use channels.msteams.accounts.<id> for each Teams bot registration, then route
that account with bindings[].match.accountId. Root settings are shared
defaults; account settings override them.
{
bindings: [
{ agentId: "main", match: { channel: "msteams", accountId: "default" } },
{ agentId: "support", match: { channel: "msteams", accountId: "support" } },
],
channels: {
msteams: {
enabled: true,
tenantId: "<TENANT_ID>",
webhook: { path: "/api/messages" },
dmPolicy: "allowlist",
allowFrom: ["00000000-0000-0000-0000-000000000000"],
defaultAccount: "default",
accounts: {
default: {
appId: "<PRIMARY_CLIENT_ID>",
appPassword: "<PRIMARY_CLIENT_SECRET>",
},
support: {
appId: "<SUPPORT_CLIENT_ID>",
appPassword: "<SUPPORT_CLIENT_SECRET>",
webhook: { path: "/api/messages/support" },
allowFrom: ["11111111-1111-1111-1111-111111111111"],
},
},
},
},
}- Enabled accounts must use unique
appIdvalues and webhook paths. All bots receive callbacks ongateway.port; separate listener ports are not required. - The default account uses the root webhook path (
/api/messageswhen omitted). A named account without an explicit path appends its normalized account ID:supportuses/api/messages/support. Set each Azure Bot messaging endpoint to its own path, for examplehttps://gateway.example.com/api/messages/support. - Named accounts must define their own
appIdandappPasswordfor secret authentication. Those fields do not inherit from the root. tenantId, federated-auth settings, access policy, team/channel allowlists, streaming, SSO, delegated auth, and delivery settings inherit from the root unless an account overrides them.legacyWebhooknever inherits into named accounts; keep compatibility listeners explicit and give each a unique port.- Existing root-level single-bot credentials remain the default account for
compatibility. Configure either that root identity or
accounts.default, not both.
Environment variables
These auth-related config keys can be set via environment variables instead of openclaw.json for the default account only. Named accounts must define their bot identity in config (other keys, such as groupPolicy or historyLimit, are config-only):
| Env var | Config key | Notes |
|---|---|---|
MSTEAMS_APP_ID | appId | |
MSTEAMS_APP_PASSWORD | appPassword | |
MSTEAMS_TENANT_ID | tenantId | |
MSTEAMS_AUTH_TYPE | authType | "secret" or "federated" |
MSTEAMS_CERTIFICATE_PATH | certificatePath | federated + certificate |
MSTEAMS_CERTIFICATE_THUMBPRINT | certificateThumbprint | accepted, not required for auth |
MSTEAMS_USE_MANAGED_IDENTITY | useManagedIdentity | federated + managed identity |
MSTEAMS_MANAGED_IDENTITY_CLIENT_ID | managedIdentityClientId | user-assigned managed identity only |
History context
channels.msteams.historyLimitcontrols how many recent channel/group messages are wrapped into the prompt. Falls back tomessages.groupChat.historyLimit, then defaults to 50. Set0to disable.- Graph thread context adds the parent and up to the oldest 50 replies alongside recent channel history. It excludes the triggering message and keeps history separate from the sender's command text, so commands quoted in history do not execute. Long fetched messages retain their beginning and end within the prompt's per-message limit.
- Thread and quoted attachment context follow
channels.msteams.contextVisibility, falling back tochannels.defaults.contextVisibility, thenall. Useallowlistto filter both by sender allowlists (allowFrom/groupAllowFrom), orallowlist_quoteto filter thread history while permitting quoted context. - DM history can be limited with
channels.msteams.dmHistoryLimit(user turns). Per-user overrides:channels.msteams.dms["<user_id>"].historyLimit.
Configuration
Key settings (see /gateway/configuration for shared channel patterns):
channels.msteams.enabled: enable/disable the channel.channels.msteams.defaultAccount: account used whenaccountIdis omitted.channels.msteams.accounts.<id>.enabled: enable/disable one Teams bot account.channels.msteams.accounts.<id>.appId,channels.msteams.accounts.<id>.appPassword,channels.msteams.accounts.<id>.webhook.path: per-bot identity, secret, and Gateway callback path.channels.msteams.appId,channels.msteams.appPassword,channels.msteams.tenantId: bot credentials.channels.msteams.cloud: Teams SDK cloud environment (Public,USGov,USGovDoD, orChina; defaultPublic). Set withserviceUrlfor USGov/DoD SDK clouds; China uses the SDK preset and stored Azure China Bot Framework conversation references, with Graph-backed helpers disabled until Azure China Graph routing ships.channels.msteams.serviceUrl: Bot Connector service URL boundary for SDK proactive operations. Public cloud uses the SDK default; set for GCC (https://smba.infra.gcc.teams.microsoft.com/teams), GCC High, or DoD. China accepts Azure China Bot Framework channel hosts when the stored conversation reference comes from Teams operated by 21Vianet.channels.msteams.webhook.path: Gateway HTTP route (omitted or empty uses/api/messages), served ongateway.port(default18789).channels.msteams.legacyWebhook: explicit compatibility listener.{ port, host? }selects an endpoint, preserving the previous wildcard bind whenhostis omitted. Omitted orfalseopens no separate listener.channels.msteams.dmPolicy:pairing | allowlist | open | disabled(defaultpairing).channels.msteams.allowFrom: DM allowlist (AAD object IDs recommended). Stable AAD object IDs also authorize approval actions. The wizard resolves names to IDs during setup when Graph access is available.channels.msteams.defaultTo: default outbound target; a stable AAD object ID can also authorize approval actions.channels.msteams.dangerouslyAllowNameMatching: break-glass toggle to re-enable mutable UPN/display-name matching and direct team/channel name routing.channels.msteams.textChunkLimit: outbound text chunk size in characters (default4000, and hard-capped at4000regardless of a higher configured value).channels.msteams.streaming.chunkMode:length(default) ornewlineto split on blank lines (paragraph boundaries) before length chunking.channels.msteams.mediaAllowHosts: allowlist for inbound attachment hosts (defaults to Microsoft/Teams domains: Graph, SharePoint/OneDrive, Teams CDN, Bot Framework, Azure Media Services).channels.msteams.mediaAuthAllowHosts: allowlist for attaching Authorization headers on media retries (defaults to Graph + Bot Framework hosts).channels.msteams.graphMediaFallback: opt into Graph message lookups when channel/group HTML omits file markers (defaultfalse; see Channel/group file recovery).channels.msteams.mediaMaxMb: per-channel media size limit override in MB. Falls back toagents.defaults.mediaMaxMbwhen unset.channels.msteams.requireMention: require @mention in channels/groups (defaulttrue).channels.msteams.requireMentionInBotThreads: override mention gating in channel threads rooted at this bot's tracked messages. Omitted preserves current behavior; see Bot-created threads.channels.msteams.replyStyle:thread | top-level(see Reply style).channels.msteams.teams.<teamId>.replyStyle: per-team override.channels.msteams.teams.<teamId>.requireMention: per-team override.channels.msteams.teams.<teamId>.requireMentionInBotThreads: per-team bot-thread override.channels.msteams.teams.<teamId>.tools: default per-team tool policy overrides (allow/deny/alsoAllow) used when a channel override is missing.channels.msteams.teams.<teamId>.toolsBySender: default per-team per-sender tool policy overrides ("*"wildcard supported).channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle: per-channel override.channels.msteams.teams.<teamId>.channels.<conversationId>.requireMention: per-channel override.channels.msteams.teams.<teamId>.channels.<conversationId>.requireMentionInBotThreads: per-channel bot-thread override.channels.msteams.teams.<teamId>.channels.<conversationId>.tools: per-channel tool policy overrides (allow/deny/alsoAllow).channels.msteams.teams.<teamId>.channels.<conversationId>.toolsBySender: per-channel per-sender tool policy overrides ("*"wildcard supported).toolsBySenderkeys use explicit prefixes:channel:,id:,e164:,username:,name:. Runopenclaw doctor --fixto migrate retired unprefixed keys toid:entries.channels.msteams.authType: authentication type -"secret"(default) or"federated".channels.msteams.certificatePath: path to PEM certificate file (federated + certificate auth).channels.msteams.certificateThumbprint: certificate thumbprint; accepted, not required for auth.channels.msteams.useManagedIdentity: enable managed identity auth (federated mode).channels.msteams.managedIdentityClientId: client ID for user-assigned managed identity.channels.msteams.sharePointSiteId: SharePoint site ID for file uploads in group chats/channels (see Sending files in group chats).channels.msteams.welcomeCard,channels.msteams.groupWelcomeCard,channels.msteams.promptStarters: welcome Adaptive Card shown on first DM/group contact, and its suggested prompt buttons.channels.msteams.responsePrefix: text prefixed to outbound replies.channels.msteams.feedbackEnabled(defaulttrue),channels.msteams.feedbackReflection(defaulttrue),channels.msteams.feedbackReflectionCooldownMs: thumbs-up/down feedback on replies and the negative-feedback reflection follow-up.channels.msteams.sso,channels.msteams.delegatedAuth: Bot Framework OAuth connection and delegated Graph scopes for SSO-backed flows;sso.enabled: truerequiressso.connectionName.
Migrating an existing webhook endpoint
Teams webhooks now share the Gateway HTTP listener. The Teams SDK still verifies
Azure JWT signatures; callers do not supply a Gateway token. Keep the public
HTTPS messaging endpoint in Azure Bot and change its reverse-proxy upstream to
Gateway port 18789 (or your gateway.port), preserving /api/messages or your
configured webhook.path. If you expose a port directly, update Azure Bot's
messaging endpoint to the public HTTPS URL that reaches this Gateway route.
Doctor pins legacyWebhook: { port: 3978 } once for an enabled Teams channel on
an existing installation that relied on the implicit port. Evidence of prior
operation is required; fresh installations open no separate listener. An
explicitly configured webhook.port moves to legacyWebhook.port through
Doctor's normal config backup and write flow. Pinned compatibility listeners
forward into the same Gateway route and JWT validation.
The deprecated TypeScript webhook.port input remains source-compatible until
the next Plugin SDK major. Runtime config uses legacyWebhook; run
openclaw doctor --fix to migrate the old key.
After confirming a delivery through the Gateway port, remove the
channels.msteams.legacyWebhook pin and any old firewall or Compose port mapping.
Keep meta.migrations.webhookListeners, which Doctor saves with the pin, so later
runs do not recreate it. See webhook migrations
for included and read-only config sources.
Explicit false also disables the listener. Doctor and startup print the
Gateway route and the setting to remove.
For an existing installation that supplies Teams credentials only through
environment variables, Doctor preserves the endpoint without enabling Teams in
the source config. If those credentials are visible only to the Gateway service,
Doctor leaves the decision pending until Gateway startup. Continue using
gateway run --ambient-channels to activate it. A listener setting alone no
longer enables Teams; use enabled: true for a permanent opt-in. Doctor adds that
flag to authored listener settings that previously implied activation, including
an explicit legacyWebhook: false. It does not add the flag to a generated pin
whose migration completion is already recorded.
A custom path also accepts the older /api/messages alias with its existing
deprecation warning when that route is available. If another plugin owns the
alias, startup logs the conflict and keeps serving the configured path. Update
Azure Bot to the configured path.
The Gateway reserves /health, /healthz, /ready, /readyz, /startup, and
/startupz for checks, including URLs with query strings. If your former Teams
callback uses one of these paths, set webhook.path to /api/messages and update
Azure Bot or the proxy upstream to match. Doctor reports this conflict. The
compatibility listener keeps the old endpoint working; startup refuses the
unusable Gateway route when no explicit legacy listener is configured. Verify
the replacement before removing that listener's pin.
Paths under /api/channels require Gateway authentication on the main listener,
including encoded spellings. Teams callbacks authenticate with Azure JWTs, so
use /api/messages instead. Doctor and startup report the same callback-change
action; the compatibility listener continues serving the old path until that
cutover is complete.
Express parameter, wildcard, and brace patterns continue working on the legacy
port. Gateway route registration uses literal paths. Before disabling the legacy
listener for a pattern, change the existing webhook.path to /api/messages and
update Azure Bot or your proxy. Doctor and startup identify these patterns; without
an explicit legacy listener, startup reports the required change instead of silently
dropping callbacks.