Microsoft Teams (plugin)

“Abandon all hope, ye who enter here.”

Actualizado: 2026-01-21

Estado: texto + adjuntos en mensajes directos son compatibles; el envío de archivos en canales/grupos requiere sharePointSiteId + permisos de Graph (consulta Envío de archivos en chats de grupo). Las encuestas se envían como Adaptive Cards.

Plugin obligatorio

Microsoft Teams se distribuye como plugin y no viene incluido en la instalación base.

Cambio importante (2026.1.15): MS Teams se movió fuera del core. Si lo usas, debes instalar el plugin.

Motivo: mantiene las instalaciones base más ligeras y permite que las dependencias de MS Teams se actualicen de forma independiente.

Instalación por CLI (registro npm):

openclaw plugins install @openclaw/msteams

Checkout local (cuando se ejecuta desde un repositorio git):

openclaw plugins install ./extensions/msteams

Si eliges Teams durante la configuración/onboarding y se detecta un checkout de git, OpenClaw ofrecerá la ruta de instalación local automáticamente.

Detalles: Plugins

Configuración rápida (principiante)

  1. Instala el plugin de Microsoft Teams.
  2. Crea un Azure Bot (App ID + client secret + tenant ID).
  3. Configura OpenClaw con esas credenciales.
  4. Expón /api/messages (puerto 3978 por defecto) mediante una URL pública o túnel.
  5. Instala el paquete de la app de Teams e inicia el gateway.

Configuración mínima:

{
  channels: {
    msteams: {
      enabled: true,
      appId: "<APP_ID>",
      appPassword: "<APP_PASSWORD>",
      tenantId: "<TENANT_ID>",
      webhook: { port: 3978, path: "/api/messages" },
    },
  },
}

Nota: los chats de grupo están bloqueados por defecto (channels.msteams.groupPolicy: "allowlist"). Para permitir respuestas en grupos, establece channels.msteams.groupAllowFrom (o usa groupPolicy: "open" para permitir a cualquier miembro, controlado por mención).

Objetivos

  • Hablar con OpenClaw a través de mensajes directos de Teams, chats de grupo o canales.
  • Mantener el enrutamiento determinístico: las respuestas siempre regresan al canal del que llegaron.
  • Usar comportamiento seguro de canal por defecto (menciones requeridas a menos que se configure de otra forma).

Escrituras de configuración

Por defecto, Microsoft Teams puede escribir actualizaciones de configuración activadas por /config set|unset (requiere commands.config: true).

Desactivar con:

{
  channels: { msteams: { configWrites: false } },
}

Control de acceso (mensajes directos + grupos)

Acceso a mensajes directos

  • Por defecto: channels.msteams.dmPolicy = "pairing". Los remitentes desconocidos se ignoran hasta ser aprobados.
  • channels.msteams.allowFrom debe usar IDs de objeto AAD estables.
  • Los UPNs/nombres de pantalla son mutables; la coincidencia directa está desactivada por defecto y solo se activa con channels.msteams.dangerouslyAllowNameMatching: true.
  • El asistente puede resolver nombres a IDs vía Microsoft Graph cuando las credenciales lo permiten.

Acceso a grupos

  • Por defecto: channels.msteams.groupPolicy = "allowlist" (bloqueado a menos que añadas groupAllowFrom). Usa channels.defaults.groupPolicy para sobrescribir el predeterminado cuando no está establecido.
  • channels.msteams.groupAllowFrom controla qué remitentes pueden activar en chats de grupo/canales (recurre a channels.msteams.allowFrom).
  • Establece groupPolicy: "open" para permitir a cualquier miembro (controlado por mención por defecto).
  • Para no permitir ningún canal, establece channels.msteams.groupPolicy: "disabled".

Ejemplo:

{
  channels: {
    msteams: {
      groupPolicy: "allowlist",
      groupAllowFrom: ["[email protected]"],
    },
  },
}

Lista de permitidos de teams + canales

  • Limita las respuestas de grupo/canal listando teams y canales bajo channels.msteams.teams.
  • Las claves deben usar IDs estables de team e IDs de conversación de canal.
  • Cuando groupPolicy="allowlist" y hay una lista de teams presente, solo se aceptan los teams/canales listados (controlados por mención).
  • El asistente de configuración acepta entradas Team/Channel y las almacena por ti.
  • Al iniciar, OpenClaw resuelve nombres de team/canal y de la lista de usuarios a IDs (cuando los permisos de Graph lo permiten) y registra el mapeo; los nombres de team/canal no resueltos se mantienen como fueron escritos pero se ignoran para el enrutamiento por defecto a menos que channels.msteams.dangerouslyAllowNameMatching: true esté activado.

Ejemplo:

{
  channels: {
    msteams: {
      groupPolicy: "allowlist",
      teams: {
        "My Team": {
          channels: {
            General: { requireMention: true },
          },
        },
      },
    },
  },
}

Cómo funciona

  1. Instala el plugin de Microsoft Teams.
  2. Crea un Azure Bot (App ID + secret + tenant ID).
  3. Construye un paquete de app de Teams que referencie el bot e incluya los permisos RSC descritos abajo.
  4. Carga/instala la app de Teams en un team (o alcance personal para mensajes directos).
  5. Configura msteams en ~/.openclaw/openclaw.json (o variables de entorno) e inicia el gateway.
  6. El gateway escucha tráfico de webhook del Bot Framework en /api/messages por defecto.

Configuración del Azure Bot (Requisitos previos)

Antes de configurar OpenClaw, necesitas crear un recurso de Azure Bot.

Paso 1: Crear Azure Bot

  1. Ve a Crear Azure Bot

  2. Completa la pestaña Basics:

    CampoValor
    Bot handleNombre de tu bot, p. ej., openclaw-msteams (debe ser único)
    SubscriptionSelecciona tu suscripción de Azure
    Resource groupCrea nuevo o usa existente
    Pricing tierFree para desarrollo/pruebas
    Type of AppSingle Tenant (recomendado - ver nota abajo)
    Creation typeCreate new Microsoft App ID

Nota: La creación de nuevos bots multi-tenant fue descontinuada después del 2025-07-31. Usa Single Tenant para bots nuevos.

  1. Haz clic en Review + createCreate (espera ~1-2 minutos)

Paso 2: Obtener credenciales

  1. Ve a tu recurso Azure Bot → Configuration
  2. Copia Microsoft App ID → este es tu appId
  3. Haz clic en Manage Password → ve al App Registration
  4. En Certificates & secretsNew client secret → copia el Value → este es tu appPassword
  5. Ve a Overview → copia Directory (tenant) ID → este es tu tenantId

Paso 3: Configurar Messaging Endpoint

  1. En Azure Bot → Configuration
  2. Establece Messaging endpoint a tu URL de webhook:
    • Producción: https://your-domain.com/api/messages
    • Desarrollo local: usa un túnel (consulta Desarrollo local abajo)

Paso 4: Habilitar canal de Teams

  1. En Azure Bot → Channels
  2. Haz clic en Microsoft Teams → Configure → Save
  3. Acepta los Términos de Servicio

Desarrollo local (Tunneling)

Teams no puede alcanzar localhost. Usa un túnel para desarrollo local:

Opción A: ngrok

ngrok http 3978
# Copia la URL https, p. ej., https://abc123.ngrok.io
# Establece messaging endpoint a: https://abc123.ngrok.io/api/messages

Opción B: Tailscale Funnel

tailscale funnel 3978
# Usa tu URL de Tailscale funnel como messaging endpoint

Portal de Desarrolladores de Teams (alternativa)

En lugar de crear manualmente un ZIP de manifiesto, puedes usar el Portal de Desarrolladores de Teams:

  1. Haz clic en + New app
  2. Completa la información básica (nombre, descripción, info del desarrollador)
  3. Ve a App featuresBot
  4. Selecciona Enter a bot ID manually y pega tu App ID del Azure Bot
  5. Marca los alcances: Personal, Team, Group Chat
  6. Haz clic en DistributeDownload app package
  7. En Teams: AppsManage your appsUpload a custom app → selecciona el ZIP

Esto suele ser más sencillo que editar manifiestos JSON manualmente.

Probar el bot

Opción A: Azure Web Chat (verificar webhook primero)

  1. En Azure Portal → tu recurso Azure Bot → Test in Web Chat
  2. Envía un mensaje — deberías ver una respuesta
  3. Esto confirma que tu endpoint de webhook funciona antes de la configuración de Teams

Opción B: Teams (después de la instalación de la app)

  1. Instala la app de Teams (sideload o catálogo de la organización)
  2. Busca el bot en Teams y envía un mensaje directo
  3. Revisa los registros del gateway para actividad entrante

Configuración (solo texto mínimo)

  1. Instala el plugin de Microsoft Teams

    • Desde npm: openclaw plugins install @openclaw/msteams
    • Desde un checkout local: openclaw plugins install ./extensions/msteams
  2. Registro del bot

    • Crea un Azure Bot (ver arriba) y anota:
      • App ID
      • Client secret (App password)
      • Tenant ID (single-tenant)
  3. Manifiesto de app de Teams

    • Incluye una entrada bot con botId = <App ID>.
    • Alcances: personal, team, groupChat.
    • supportsFiles: true (obligatorio para manejo de archivos en alcance personal).
    • Añade permisos RSC (abajo).
    • Crea iconos: outline.png (32x32) y color.png (192x192).
    • Comprime los tres archivos juntos: manifest.json, outline.png, color.png.
  4. Configura OpenClaw

    {
      "msteams": {
        "enabled": true,
        "appId": "<APP_ID>",
        "appPassword": "<APP_PASSWORD>",
        "tenantId": "<TENANT_ID>",
        "webhook": { "port": 3978, "path": "/api/messages" }
      }
    }

    También puedes usar variables de entorno en lugar de claves de configuración:

    • MSTEAMS_APP_ID
    • MSTEAMS_APP_PASSWORD
    • MSTEAMS_TENANT_ID
  5. Endpoint del bot

    • Establece el Messaging Endpoint del Azure Bot a:
      • https://<host>:3978/api/messages (o tu ruta/puerto elegido).
  6. Ejecuta el gateway

    • El canal de Teams se inicia automáticamente cuando el plugin está instalado y la configuración msteams existe con credenciales.

Contexto de historial

  • channels.msteams.historyLimit controla cuántos mensajes recientes de canal/grupo se incluyen en el prompt.
  • Recurre a messages.groupChat.historyLimit. Establece 0 para desactivar (por defecto 50).
  • El historial de mensajes directos puede limitarse con channels.msteams.dmHistoryLimit (turnos de usuario). Excepciones por usuario: channels.msteams.dms["<user_id>"].historyLimit.

Permisos RSC actuales de Teams (Manifiesto)

Estos son los permisos resourceSpecific existentes en nuestro manifiesto de app de Teams. Solo aplican dentro del team/chat donde la app está instalada.

Para canales (alcance de team):

  • ChannelMessage.Read.Group (Application) - recibir todos los mensajes del canal sin @mención
  • ChannelMessage.Send.Group (Application)
  • Member.Read.Group (Application)
  • Owner.Read.Group (Application)
  • ChannelSettings.Read.Group (Application)
  • TeamMember.Read.Group (Application)
  • TeamSettings.Read.Group (Application)

Para chats de grupo:

  • ChatMessage.Read.Chat (Application) - recibir todos los mensajes del chat de grupo sin @mención

Ejemplo de manifiesto de Teams (redactado)

Ejemplo mínimo y válido con los campos obligatorios. Reemplaza los IDs y URLs.

{
  "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.23/MicrosoftTeams.schema.json",
  "manifestVersion": "1.23",
  "version": "1.0.0",
  "id": "00000000-0000-0000-0000-000000000000",
  "name": { "short": "OpenClaw" },
  "developer": {
    "name": "Your Org",
    "websiteUrl": "https://example.com",
    "privacyUrl": "https://example.com/privacy",
    "termsOfUseUrl": "https://example.com/terms"
  },
  "description": { "short": "OpenClaw in Teams", "full": "OpenClaw in Teams" },
  "icons": { "outline": "outline.png", "color": "color.png" },
  "accentColor": "#5B6DEF",
  "bots": [
    {
      "botId": "11111111-1111-1111-1111-111111111111",
      "scopes": ["personal", "team", "groupChat"],
      "isNotificationOnly": false,
      "supportsCalling": false,
      "supportsVideo": false,
      "supportsFiles": true
    }
  ],
  "webApplicationInfo": {
    "id": "11111111-1111-1111-1111-111111111111"
  },
  "authorization": {
    "permissions": {
      "resourceSpecific": [
        { "name": "ChannelMessage.Read.Group", "type": "Application" },
        { "name": "ChannelMessage.Send.Group", "type": "Application" },
        { "name": "Member.Read.Group", "type": "Application" },
        { "name": "Owner.Read.Group", "type": "Application" },
        { "name": "ChannelSettings.Read.Group", "type": "Application" },
        { "name": "TeamMember.Read.Group", "type": "Application" },
        { "name": "TeamSettings.Read.Group", "type": "Application" },
        { "name": "ChatMessage.Read.Chat", "type": "Application" }
      ]
    }
  }
}

Consideraciones del manifiesto (campos obligatorios)

  • bots[].botId debe coincidir con el App ID del Azure Bot.
  • webApplicationInfo.id debe coincidir con el App ID del Azure Bot.
  • bots[].scopes debe incluir las superficies que planeas usar (personal, team, groupChat).
  • bots[].supportsFiles: true es obligatorio para el manejo de archivos en alcance personal.
  • authorization.permissions.resourceSpecific debe incluir lectura/envío de canal si quieres tráfico de canal.

Actualizar una app existente

Para actualizar una app de Teams ya instalada (p. ej., para añadir permisos RSC):

  1. Actualiza tu manifest.json con la nueva configuración
  2. Incrementa el campo version (p. ej., 1.0.01.1.0)
  3. Vuelve a comprimir el manifiesto con los iconos (manifest.json, outline.png, color.png)
  4. Carga el nuevo zip:
    • Opción A (Centro de administración de Teams): Centro de administración de Teams → Teams apps → Manage apps → busca tu app → Upload new version
    • Opción B (Sideload): En Teams → Apps → Manage your apps → Upload a custom app
  5. Para canales de team: reinstala la app en cada team para que los nuevos permisos surtan efecto
  6. Cierra completamente y reinicia Teams (no solo cerrar la ventana) para limpiar los metadatos de la app en caché

Capacidades: solo RSC vs Graph

Con solo RSC de Teams (app instalada, sin permisos de Graph API)

Funciona:

  • Leer contenido de texto de mensajes de canal.
  • Enviar contenido de texto de mensajes de canal.
  • Recibir adjuntos de archivo en mensajes directos (personales).

No funciona:

  • Contenidos de imágenes o archivos de canales/grupos (el payload solo incluye un stub HTML).
  • Descargar adjuntos almacenados en SharePoint/OneDrive.
  • Leer historial de mensajes (más allá del evento de webhook en vivo).

Con RSC de Teams + permisos de aplicación Microsoft Graph

Añade:

  • Descargar contenidos hospedados (imágenes pegadas en mensajes).
  • Descargar adjuntos de archivos almacenados en SharePoint/OneDrive.
  • Leer historial de mensajes de canal/chat vía Graph.

RSC vs Graph API

CapacidadPermisos RSCGraph API
Mensajes en tiempo realSí (vía webhook)No (solo polling)
Mensajes históricosNoSí (puede consultar historial)
Complejidad de setupSolo manifiestoRequiere consentimiento admin + flujo de token
Funciona offlineNo (debe estar ejecutándose)Sí (consulta en cualquier momento)

Resumen: RSC es para escucha en tiempo real; Graph API es para acceso histórico. Para recuperar mensajes perdidos mientras está offline, necesitas Graph API con ChannelMessage.Read.All (requiere consentimiento del administrador).

Medios + historial con Graph (obligatorio para canales)

Si necesitas imágenes/archivos en canales o quieres obtener historial de mensajes, debes habilitar permisos de Microsoft Graph y otorgar consentimiento del administrador.

  1. En Entra ID (Azure AD) App Registration, añade permisos de aplicación de Microsoft Graph:
    • ChannelMessage.Read.All (adjuntos e historial de canal)
    • Chat.Read.All o ChatMessage.Read.All (chats de grupo)
  2. Otorga consentimiento del administrador para el tenant.
  3. Incrementa la versión del manifiesto de la app de Teams, vuelve a cargar y reinstala la app en Teams.
  4. Cierra completamente y reinicia Teams para limpiar los metadatos de la app en caché.

Permiso adicional para menciones de usuario: Las @menciones de usuario funcionan de forma nativa para usuarios en la conversación. Sin embargo, si quieres buscar dinámicamente y mencionar usuarios que no están en la conversación actual, añade el permiso User.Read.All (Application) y otorga consentimiento del administrador.

Limitaciones conocidas

Timeouts de webhook

Teams entrega mensajes vía webhook HTTP. Si el procesamiento toma demasiado tiempo (p. ej., respuestas LLM lentas), puedes ver:

  • Timeouts del gateway
  • Teams reintentando el mensaje (causando duplicados)
  • Respuestas perdidas

OpenClaw maneja esto devolviendo rápidamente y enviando respuestas de forma proactiva, pero respuestas muy lentas aún pueden causar problemas.

Formato

El markdown de Teams es más limitado que el de Slack o Discord:

  • El formato básico funciona: negrita, cursiva, código, enlaces
  • Markdown complejo (tablas, listas anidadas) puede no renderizarse correctamente
  • Las Adaptive Cards son compatibles para encuestas y envíos de tarjetas arbitrarias (ver abajo)

Configuración

Configuraciones clave (consulta /gateway/configuration para patrones compartidos de canal):

  • channels.msteams.enabled: activar/desactivar el canal.
  • channels.msteams.appId, channels.msteams.appPassword, channels.msteams.tenantId: credenciales del bot.
  • channels.msteams.webhook.port (por defecto 3978)
  • channels.msteams.webhook.path (por defecto /api/messages)
  • channels.msteams.dmPolicy: pairing | allowlist | open | disabled (por defecto: pairing)
  • channels.msteams.allowFrom: lista de permitidos para MD (se recomiendan IDs de objeto AAD). El asistente resuelve nombres a IDs durante la configuración cuando el acceso a Graph está disponible.
  • channels.msteams.dangerouslyAllowNameMatching: toggle de emergencia para rehabilitar la coincidencia mutable de UPN/nombre de pantalla y el enrutamiento directo por nombre de team/canal.
  • channels.msteams.textChunkLimit: tamaño de fragmento de texto saliente.
  • channels.msteams.chunkMode: length (predeterminado) o newline para dividir en líneas en blanco (límites de párrafo) antes de la división por longitud.
  • channels.msteams.mediaAllowHosts: lista de permitidos para hosts de adjuntos entrantes (por defecto dominios de Microsoft/Teams).
  • channels.msteams.mediaAuthAllowHosts: lista de permitidos para adjuntar encabezados Authorization en reintentos de medios (por defecto hosts de Graph + Bot Framework).
  • channels.msteams.requireMention: requiere @mención en canales/grupos (por defecto true).
  • channels.msteams.replyStyle: thread | top-level (consulta Estilo de respuesta).
  • channels.msteams.teams.<teamId>.replyStyle: excepción por team.
  • channels.msteams.teams.<teamId>.requireMention: excepción por team.
  • channels.msteams.teams.<teamId>.tools: excepciones predeterminadas de política de herramientas por team (allow/deny/alsoAllow) usadas cuando falta una excepción de canal.
  • channels.msteams.teams.<teamId>.toolsBySender: excepciones de política de herramientas por remitente por team (se admite comodín "*").
  • channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle: excepción por canal.
  • channels.msteams.teams.<teamId>.channels.<conversationId>.requireMention: excepción por canal.
  • channels.msteams.teams.<teamId>.channels.<conversationId>.tools: excepciones de política de herramientas por canal (allow/deny/alsoAllow).
  • channels.msteams.teams.<teamId>.channels.<conversationId>.toolsBySender: excepciones de política de herramientas por remitente por canal (se admite comodín "*").
  • Las claves de toolsBySender deben usar prefijos explícitos: id:, e164:, username:, name: (las claves legacy sin prefijo aún se mapean solo a id:).
  • channels.msteams.sharePointSiteId: ID del sitio SharePoint para cargas de archivos en chats de grupo/canales (consulta Envío de archivos en chats de grupo).

Enrutamiento y sesiones

  • Las claves de sesión siguen el formato estándar de agente (consulta /concepts/session):
    • Los mensajes directos comparten la sesión principal (agent:<agentId>:<mainKey>).
    • Los mensajes de canal/grupo usan el ID de conversación:
      • agent:<agentId>:msteams:channel:<conversationId>
      • agent:<agentId>:msteams:group:<conversationId>

Estilo de respuesta: hilos vs posts

Teams introdujo recientemente dos estilos de UI de canal sobre el mismo modelo de datos subyacente:

EstiloDescripciónreplyStyle recomendado
Posts (clásico)Los mensajes aparecen como tarjetas con respuestas en hilo debajothread (predeterminado)
Threads (tipo Slack)Los mensajes fluyen linealmente, más como Slacktop-level

El problema: La API de Teams no expone qué estilo de UI usa un canal. Si usas el replyStyle incorrecto:

  • thread en un canal estilo Threads → las respuestas aparecen anidadas de forma incómoda
  • top-level en un canal estilo Posts → las respuestas aparecen como posts de nivel superior separados en vez de en el hilo

Solución: Configura replyStyle por canal según cómo esté configurado el canal:

{
  "msteams": {
    "replyStyle": "thread",
    "teams": {
      "19:[email protected]": {
        "channels": {
          "19:[email protected]": {
            "replyStyle": "top-level"
          }
        }
      }
    }
  }
}

Adjuntos e imágenes

Limitaciones actuales:

  • Mensajes directos: las imágenes y adjuntos de archivo funcionan vía las API de archivos del bot de Teams.
  • Canales/grupos: los adjuntos residen en almacenamiento M365 (SharePoint/OneDrive). El payload del webhook solo incluye un stub HTML, no los bytes reales del archivo. Se requieren permisos de Graph API para descargar adjuntos de canal.

Sin permisos de Graph, los mensajes de canal con imágenes se recibirán como solo texto (el contenido de la imagen no es accesible para el bot). Por defecto, OpenClaw solo descarga medios de hostnames de Microsoft/Teams. Sobrescribe con channels.msteams.mediaAllowHosts (usa ["*"] para permitir cualquier host). Los encabezados Authorization solo se adjuntan para hosts en channels.msteams.mediaAuthAllowHosts (por defecto hosts de Graph + Bot Framework). Mantén esta lista estricta (evita sufijos multi-tenant).

Envío de archivos en chats de grupo

Los bots pueden enviar archivos en mensajes directos usando el flujo FileConsentCard (integrado). Sin embargo, enviar archivos en chats de grupo/canales requiere configuración adicional:

ContextoCómo se envían los archivosConfiguración necesaria
Mensajes directosFileConsentCard → el usuario acepta → el bot cargaFunciona sin configuración adicional
Chats de grupo/canalesCargar a SharePoint → compartir enlaceRequiere sharePointSiteId + permisos de Graph
Imágenes (cualquier contexto)Codificadas en base64 inlineFunciona sin configuración adicional

Por qué los chats de grupo necesitan SharePoint

Los bots no tienen un drive de OneDrive personal (el endpoint /me/drive de Graph API no funciona para identidades de aplicación). Para enviar archivos en chats de grupo/canales, el bot carga a un sitio de SharePoint y crea un enlace de compartición.

Configuración

  1. Añade permisos de Graph API en Entra ID (Azure AD) → App Registration:

    • Sites.ReadWrite.All (Application) - cargar archivos a SharePoint
    • Chat.Read.All (Application) - opcional, habilita enlaces de compartición por usuario
  2. Otorga consentimiento del administrador para el tenant.

  3. Obtén tu ID de sitio SharePoint:

    # Via Graph Explorer o curl con un token válido:
    curl -H "Authorization: Bearer $TOKEN" \
      "https://graph.microsoft.com/v1.0/sites/{hostname}:/{site-path}"
    
    # Ejemplo: para un sitio en "contoso.sharepoint.com/sites/BotFiles"
    curl -H "Authorization: Bearer $TOKEN" \
      "https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/BotFiles"
    
    # La respuesta incluye: "id": "contoso.sharepoint.com,guid1,guid2"
  4. Configura OpenClaw:

    {
      channels: {
        msteams: {
          // ... otra configuración ...
          sharePointSiteId: "contoso.sharepoint.com,guid1,guid2",
        },
      },
    }

Comportamiento de compartición

PermisoComportamiento de compartición
Solo Sites.ReadWrite.AllEnlace de compartición a nivel de organización (cualquiera en la org puede acceder)
Sites.ReadWrite.All + Chat.Read.AllEnlace de compartición por usuario (solo los miembros del chat pueden acceder)

La compartición por usuario es más segura ya que solo los participantes del chat pueden acceder al archivo. Si falta el permiso Chat.Read.All, el bot recurre a la compartición a nivel de organización.

Comportamiento de respaldo

EscenarioResultado
Chat de grupo + archivo + sharePointSiteId configuradoCargar a SharePoint, enviar enlace de compartición
Chat de grupo + archivo + sin sharePointSiteIdIntentar carga a OneDrive (puede fallar), enviar solo texto
Chat personal + archivoFlujo FileConsentCard (funciona sin SharePoint)
Cualquier contexto + imagenCodificada en base64 inline (funciona sin SharePoint)

Ubicación de archivos almacenados

Los archivos cargados se almacenan en una carpeta /OpenClawShared/ en la biblioteca de documentos predeterminada del sitio SharePoint configurado.

Encuestas (Adaptive Cards)

OpenClaw envía encuestas de Teams como Adaptive Cards (no hay API nativa de encuestas de Teams).

  • CLI: openclaw message poll --channel msteams --target conversation:<id> ...
  • Los votos se registran por el gateway en ~/.openclaw/msteams-polls.json.
  • El gateway debe permanecer en línea para registrar votos.
  • Las encuestas aún no publican automáticamente resúmenes de resultados (inspecciona el archivo de almacenamiento si es necesario).

Adaptive Cards (arbitrarias)

Envía cualquier JSON de Adaptive Card a usuarios o conversaciones de Teams usando la herramienta message o CLI.

El parámetro card acepta un objeto JSON de Adaptive Card. Cuando se proporciona card, el texto del mensaje es opcional.

Herramienta del agente:

{
  "action": "send",
  "channel": "msteams",
  "target": "user:<id>",
  "card": {
    "type": "AdaptiveCard",
    "version": "1.5",
    "body": [{ "type": "TextBlock", "text": "Hello!" }]
  }
}

CLI:

openclaw message send --channel msteams \
  --target "conversation:19:[email protected]" \
  --card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello!"}]}'

Consulta la documentación de Adaptive Cards para el esquema y ejemplos de tarjetas. Para detalles de formato de destino, consulta Formatos de destino abajo.

Formatos de destino

Los destinos de MSTeams usan prefijos para distinguir entre usuarios y conversaciones:

Tipo de destinoFormatoEjemplo
Usuario (por ID)user:<aad-object-id>user:40a1a0ed-4ff2-4164-a219-55518990c197
Usuario (por nombre)user:<display-name>user:John Smith (requiere Graph API)
Grupo/canalconversation:<conversation-id>conversation:19:[email protected]
Grupo/canal (raw)<conversation-id>19:[email protected] (si contiene @thread)

Ejemplos de CLI:

# Enviar a un usuario por ID
openclaw message send --channel msteams --target "user:40a1a0ed-..." --message "Hello"

# Enviar a un usuario por nombre de pantalla (activa búsqueda Graph API)
openclaw message send --channel msteams --target "user:John Smith" --message "Hello"

# Enviar a un chat de grupo o canal
openclaw message send --channel msteams --target "conversation:19:[email protected]" --message "Hello"

# Enviar una Adaptive Card a una conversación
openclaw message send --channel msteams --target "conversation:19:[email protected]" \
  --card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello"}]}'

Ejemplos de herramienta del agente:

{
  "action": "send",
  "channel": "msteams",
  "target": "user:John Smith",
  "message": "Hello!"
}
{
  "action": "send",
  "channel": "msteams",
  "target": "conversation:19:[email protected]",
  "card": {
    "type": "AdaptiveCard",
    "version": "1.5",
    "body": [{ "type": "TextBlock", "text": "Hello" }]
  }
}

Nota: sin el prefijo user:, los nombres se resuelven primero como grupo/team. Usa siempre user: cuando apuntes a personas por nombre de pantalla.

Mensajería proactiva

  • Los mensajes proactivos solo son posibles después de que un usuario haya interactuado, porque almacenamos las referencias de conversación en ese momento.
  • Consulta /gateway/configuration para las políticas de dmPolicy y listas de permitidos.

IDs de team y canal (error común)

El parámetro de consulta groupId en las URLs de Teams NO es el ID del team usado para la configuración. Extrae los IDs de la ruta de la URL en su lugar:

URL de team:

https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=...
                                    └────────────────────────────┘
                                    Team ID (decodifica esta URL)

URL de canal:

https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=...
                                      └─────────────────────────┘
                                      Channel ID (decodifica esta URL)

Para la configuración:

  • Team ID = segmento de ruta después de /team/ (decodificado, p. ej., 19:[email protected])
  • Channel ID = segmento de ruta después de /channel/ (decodificado)
  • Ignora el parámetro de consulta groupId

Canales privados

Los bots tienen soporte limitado en canales privados:

FuncionalidadCanales estándarCanales privados
Instalación del botLimitada
Mensajes en tiempo real (webhook)Puede no funcionar
Permisos RSCPuede comportarse diferente
@mencionesSi el bot es accesible
Historial vía Graph APISí (con permisos)

Alternativas si los canales privados no funcionan:

  1. Usa canales estándar para las interacciones con el bot
  2. Usa mensajes directos - los usuarios siempre pueden enviar mensajes directos al bot
  3. Usa Graph API para acceso histórico (requiere ChannelMessage.Read.All)

Solución de problemas

Problemas comunes

  • Las imágenes no se muestran en canales: faltan permisos de Graph o consentimiento del administrador. Reinstala la app de Teams y cierra/reabre Teams completamente.
  • Sin respuestas en canal: las menciones son obligatorias por defecto; establece channels.msteams.requireMention=false o configura por team/canal.
  • Discrepancia de versión (Teams sigue mostrando manifiesto antiguo): elimina + vuelve a añadir la app y cierra completamente Teams para actualizar.
  • 401 Unauthorized del webhook: Esperado al probar manualmente sin JWT de Azure — significa que el endpoint es alcanzable pero la autenticación falló. Usa Azure Web Chat para probar correctamente.

Errores de carga de manifiesto

  • “Icon file cannot be empty”: el manifiesto referencia archivos de icono que son de 0 bytes. Crea iconos PNG válidos (32x32 para outline.png, 192x192 para color.png).
  • “webApplicationInfo.Id already in use”: la app aún está instalada en otro team/chat. Busca y desinstálala primero, o espera 5-10 minutos para la propagación.
  • “Something went wrong” al cargar: carga vía https://admin.teams.microsoft.com, abre las DevTools del navegador (F12) → pestaña Network, y verifica el cuerpo de la respuesta para el error real.
  • Falla el sideload: prueba “Upload an app to your org’s app catalog” en vez de “Upload a custom app” — esto a menudo evita las restricciones de sideload.

Los permisos RSC no funcionan

  1. Verifica que webApplicationInfo.id coincida exactamente con el App ID de tu bot
  2. Vuelve a cargar la app y reinstálala en el team/chat
  3. Verifica si el administrador de tu organización ha bloqueado los permisos RSC
  4. Confirma que estás usando el alcance correcto: ChannelMessage.Read.Group para teams, ChatMessage.Read.Chat para chats de grupo

Referencias