Saltar al contenido principal
Estado: listo para producción mediante WhatsApp Web (Baileys). El gateway es propietario de las sesiones vinculadas; no hay ningún canal de WhatsApp de Twilio separado.

Instalación

openclaw onboard y openclaw channels add --channel whatsapp solicitan instalar el plugin la primera vez que lo seleccionas; openclaw channels login --channel whatsapp ofrece el mismo flujo de instalación si falta el plugin. Los checkouts de desarrollo usan la ruta local del plugin; las instalaciones estables/beta instalan primero @openclaw/whatsapp desde ClawHub, con fallback a npm. El runtime de WhatsApp se distribuye fuera del paquete npm principal de OpenClaw, por lo que sus dependencias de runtime permanecen con el plugin externo. Instalación manual:
openclaw plugins install clawhub:@openclaw/whatsapp
Usa el paquete npm sin prefijo (@openclaw/whatsapp) solo para el fallback del registro; fija una versión exacta solo para una instalación reproducible.

Emparejamiento

La política de DM predeterminada es emparejamiento para remitentes desconocidos.

Solución de problemas de canales

Diagnósticos entre canales y guías de reparación.

Configuración del Gateway

Patrones y ejemplos completos de configuración de canales.

Configuración rápida

1

Configurar la política de acceso

{
  channels: {
    whatsapp: {
      dmPolicy: "pairing",
      allowFrom: ["+15551234567"],
      groupPolicy: "allowlist",
      groupAllowFrom: ["+15551234567"],
    },
  },
}
2

Vincular WhatsApp (QR)

openclaw channels login --channel whatsapp
El inicio de sesión es solo con QR. En hosts remotos o sin interfaz gráfica, ten una ruta fiable para entregar el QR activo al teléfono antes de iniciar sesión; los QR renderizados en la terminal, capturas de pantalla o adjuntos de chat pueden caducar en tránsito.Para una cuenta específica:
openclaw channels login --channel whatsapp --account work
Para adjuntar un directorio de autenticación existente/personalizado antes de iniciar sesión:
openclaw channels add --channel whatsapp --account work --auth-dir /path/to/wa-auth
openclaw channels login --channel whatsapp --account work
3

Iniciar el Gateway

openclaw gateway
4

Aprobar la primera solicitud de emparejamiento (modo de emparejamiento)

openclaw pairing list whatsapp
openclaw pairing approve whatsapp <CODE>
Las solicitudes de emparejamiento caducan después de 1 hora; las solicitudes pendientes tienen un límite de 3 por cuenta.
Se recomienda un número de WhatsApp separado (la configuración y los metadatos están optimizados para ello), pero las configuraciones con número personal/autochat son totalmente compatibles.

Patrones de despliegue

  • identidad de WhatsApp separada para OpenClaw
  • listas de permitidos de DM y límites de enrutamiento más claros
  • menor probabilidad de confusión por autochat
{
  channels: {
    whatsapp: {
      dmPolicy: "allowlist",
      allowFrom: ["+15551234567"],
    },
  },
}
La incorporación admite el modo de número personal y escribe una base compatible con autochat: dmPolicy: "allowlist", allowFrom con tu propio número incluido, selfChatMode: true. Las protecciones de autochat en runtime se basan en el número propio vinculado más allowFrom.

Modelo de runtime

  • El Gateway es propietario del socket de WhatsApp y del bucle de reconexión.
  • Un watchdog rastrea dos señales de forma independiente: la actividad sin procesar del transporte de WhatsApp Web y la actividad de mensajes de la aplicación. Una sesión silenciosa pero conectada no se reinicia solo porque no haya llegado ningún mensaje recientemente; fuerza la reconexión solo cuando los frames de transporte dejan de llegar durante una ventana interna fija (no configurable por el usuario) o los mensajes de la aplicación permanecen en silencio más allá de 4 veces el timeout normal de mensajes. Justo después de una reconexión de una sesión recientemente activa, esa primera ventana usa el timeout normal de mensajes más corto en lugar de la ventana de 4 veces. OpenClaw puede responder automáticamente a mensajes sin conexión que Baileys entrega al inicio de esa reconexión, limitado por la duración de deduplicación de los ID de mensajes entrantes; el arranque inicial conserva la protección corta contra historial obsoleto.
  • Los tiempos del socket de Baileys son explícitos bajo web.whatsapp.*: keepAliveIntervalMs (intervalo de ping de la aplicación), connectTimeoutMs (timeout del handshake de apertura), defaultQueryTimeoutMs (esperas de consultas de Baileys, además de los timeouts de envío/presencia salientes y confirmación de lectura entrante de OpenClaw).
  • Los envíos salientes requieren un listener de WhatsApp activo para la cuenta de destino; de lo contrario, los envíos fallan rápidamente.
  • Los envíos a grupos adjuntan metadatos nativos de mención para tokens @+<digits> y @<digits> (en texto y pies de medios) cuando el token coincide con los metadatos actuales de participantes, incluidos grupos respaldados por LID.
  • Los chats de estado y difusión (@status, @broadcast) se ignoran.
  • Los chats directos usan reglas de sesión de DM (session.dmScope; el valor predeterminado main colapsa los DM en la sesión principal del agente). Las sesiones de grupo se aíslan por JID (agent:<agentId>:whatsapp:group:<jid>).
  • Los canales/boletines de WhatsApp pueden ser destinos salientes explícitos mediante su JID nativo @newsletter, usando metadatos de sesión de canal (agent:<agentId>:whatsapp:channel:<jid>) en lugar de semántica de DM.
  • El transporte de WhatsApp Web respeta las variables de entorno de proxy estándar en el host del Gateway (HTTPS_PROXY, HTTP_PROXY, NO_PROXY, variantes en minúsculas). Prefiere la configuración de proxy a nivel de host sobre los ajustes por canal.
  • Con messages.removeAckAfterReply habilitado, OpenClaw borra la reacción de acuse una vez que se entrega una respuesta visible.

Llamar al solicitante actual con MeowCaller (experimental)

El plugin puede exponer whatsapp_call en turnos de agente originados en WhatsApp. Usa MeowCaller para realizar una llamada de voz de WhatsApp al solicitante autorizado actual y reproducir un mensaje TTS de OpenClaw después de que responda. La herramienta no tiene parámetro de número de destino, por lo que un prompt no puede redirigir la llamada. Deshabilitado de forma predeterminada.
MeowCaller es experimental, no tiene release etiquetado y usa una sesión de dispositivo vinculado whatsmeow emparejada por separado: no puede reutilizar las credenciales de Baileys del plugin. El emparejamiento añade otro dispositivo vinculado a la misma cuenta de WhatsApp; escanea con la identidad usada por OpenClaw. El modo de número personal/autochat no puede llamarse a sí mismo; usa un número dedicado de OpenClaw para llamar a tu número personal.
1

Habilitar llamadas experimentales

Añade actions.calls: true a la configuración del canal de WhatsApp y reinicia el Gateway:
{
  "channels": {
    "whatsapp": {
      "actions": {
        "calls": true
      }
    }
  }
}
Cuando está ausente o es false, OpenClaw no expone la herramienta whatsapp_call.
2

Instalar la CLI de MeowCaller revisada

El adaptador espera un ejecutable meowcaller en el PATH del host del Gateway. Hasta que se fusione MeowCaller PR #7, compila la rama revisada:
git clone --branch feat/send-only-notify https://github.com/steipete/meowcaller.git
cd meowcaller
git checkout 752050471fc2bf7a8cdfbf7dbd3cd4e865d85d3f
mkdir -p "$HOME/.local/bin"
go build -o "$HOME/.local/bin/meowcaller" ./cmd/meowcaller
Asegúrate de que $HOME/.local/bin esté en el PATH del servicio del Gateway. Esta revisión tiene comandos explícitos pair y notify solo de envío; notify no abre micrófono, altavoz, dispositivo de vídeo ni captura de diagnóstico. No sustituyas el comando play de la CLI de ejemplo upstream.
3

Emparejar el dispositivo vinculado de MeowCaller

Pide al agente de WhatsApp que compruebe la configuración de llamadas (la acción de estado whatsapp_call informa del directorio de estado específico de la cuenta y del comando de emparejamiento). Para la cuenta predeterminada:
state_dir="$HOME/.openclaw/credentials/whatsapp-calls/default"
mkdir -p "$state_dir"
chmod 700 "$state_dir"
meowcaller pair --store "$state_dir/wa-voip.db"
Ejecuta esto de forma interactiva, escanea el QR desde WhatsApp > Dispositivos vinculados y espera MeowCaller linked device ready. Mantén wa-voip.db privado: es la sesión de MeowCaller. Las cuentas no predeterminadas obtienen su propia ruta de almacén desde la acción de estado; en Windows, ejecuta su comando de PowerShell.
4

Configurar TTS y llamar desde WhatsApp

Configura un proveedor TTS compatible con telefonía, reinicia el Gateway y luego envía una solicitud como Call me and say the build finished. La herramienta resuelve el remitente desde contexto entrante de confianza, sintetiza un archivo WAV temporal privado, ejecuta MeowCaller durante una ventana de llamada acotada y elimina el archivo de audio después. OpenClaw pasa explícitamente el almacén de la cuenta, espera un estado de salida cero después de responder/reproducir/colgar y trata un timeout o una salida distinta de cero como una llamada de herramienta fallida.
Límites: solo llamadas de audio salientes uno a uno, sin números de destino arbitrarios, sin autenticación compartida con la conexión de chat, sin autollamadas desde el modo de número personal/autochat, audio sintetizado limitado a 60 segundos, sin confirmación de audibilidad del lado del teléfono más allá de la finalización de respuesta/reproducción/colgado de MeowCaller, y OpenClaw detiene el proceso complementario después de una ventana acotada de 115-175 segundos (que cubre las fases de conexión, respuesta, reproducción y apagado de MeowCaller).

Prompts de aprobación

WhatsApp puede renderizar prompts de aprobación de ejecución y plugin como reacciones 👍/👎, controladas por la configuración de reenvío de aprobaciones de nivel superior:
{
  approvals: {
    exec: {
      enabled: true,
      mode: "session",
    },
    plugin: {
      enabled: true,
      mode: "targets",
      targets: [{ channel: "whatsapp", to: "+15551234567" }],
    },
  },
}
approvals.exec y approvals.plugin son independientes; habilitar WhatsApp como canal solo vincula el transporte y no envía nada salvo que la familia de aprobaciones correspondiente esté habilitada y enrutada allí. El modo de sesión entrega aprobaciones nativas con emoji solo para aprobaciones que se originan en WhatsApp. El modo de destino usa la canalización compartida de reenvío para destinos explícitos y no crea un fanout de DM de aprobador separado. Las reacciones de aprobación de WhatsApp requieren aprobadores explícitos en allowFrom (o "*"). defaultTo establece destinos predeterminados ordinarios de mensajes, no una lista de aprobadores. Los comandos manuales /approve siguen pasando por la ruta normal de autorización de remitente de WhatsApp antes de la resolución de la aprobación.

Hooks de Plugin y privacidad

Los mensajes entrantes de WhatsApp pueden llevar contenido personal, números de teléfono, identificadores de grupo, nombres de remitentes y campos de correlación de sesión. WhatsApp no difunde payloads de hook message_received entrantes a plugins salvo que lo actives:
{
  channels: {
    whatsapp: {
      pluginHooks: {
        messageReceived: true,
      },
    },
  },
}
Limita la activación a una cuenta bajo channels.whatsapp.accounts.<id>.pluginHooks.messageReceived. Habilita esto solo para plugins en los que confíes con contenido e identificadores entrantes de WhatsApp.

Control de acceso y activación

channels.whatsapp.dmPolicy:
ValorComportamiento
pairing (predeterminado)Los remitentes desconocidos solicitan emparejamiento; el propietario aprueba
allowlistSolo se admiten remitentes de allowFrom
openRequiere que allowFrom incluya "*"
disabledBloquea todos los DM
allowFrom acepta números de estilo E.164 (normalizados internamente). Es solo una lista de control de acceso de remitentes de DM; no bloquea envíos salientes explícitos a JID de grupos ni JID de canal @newsletter.Anulación para varias cuentas: channels.whatsapp.accounts.<id>.dmPolicy (y .allowFrom) tienen prioridad sobre los valores predeterminados de nivel de canal para esa cuenta.Notas de runtime:
  • los emparejamientos persisten en el almacén de permitidos del canal y se fusionan con el allowFrom configurado
  • la automatización programada y la reserva de destinatarios de Heartbeat usan destinos de entrega explícitos o el allowFrom configurado; las aprobaciones de emparejamiento por DM no son destinatarios implícitos de Cron/Heartbeat
  • si no se configura ninguna lista de permitidos, el número propio vinculado se permite de forma predeterminada
  • OpenClaw nunca empareja automáticamente DMs salientes fromMe (mensajes que te envías a ti mismo desde el dispositivo vinculado)

Enlaces ACP configurados

WhatsApp admite enlaces ACP persistentes mediante bindings[] de nivel superior:
{
  bindings: [
    {
      type: "acp",
      agentId: "codex",
      match: {
        channel: "whatsapp",
        accountId: "work",
        peer: { kind: "direct", id: "+15555550123" },
      },
    },
    {
      type: "acp",
      agentId: "codex",
      match: {
        channel: "whatsapp",
        accountId: "work",
        peer: { kind: "group", id: "120363424282127706@g.us" },
      },
    },
  ],
}
Los chats directos coinciden con números E.164; los grupos coinciden con JIDs de grupo de WhatsApp. Las listas de permitidos de grupos, la política de remitentes y los controles de mención/activación se ejecutan antes de que OpenClaw garantice que exista la sesión ACP vinculada. Un enlace coincidente posee la ruta: los grupos de difusión no distribuyen ese turno a sesiones ordinarias de WhatsApp.

Comportamiento de número personal y chat propio

Cuando el número propio vinculado también está presente en allowFrom, se activan las protecciones de chat propio: se omiten confirmaciones de lectura para turnos de chat propio, se ignora el comportamiento de activación automática por JID de mención que te haría ping a ti mismo, y las respuestas predeterminadas usan [{identity.name}] (o [openclaw]) cuando messages.responsePrefix no está definido.

Normalización de mensajes y contexto

Los mensajes entrantes se envuelven en el envoltorio entrante compartido. Una respuesta citada agrega contexto con esta forma:
[Replying to <sender> id:<stanzaId>]
<quoted body or media placeholder>
[/Replying]
Los metadatos de respuesta (ReplyToId, ReplyToBody, ReplyToSender, JID/E.164 del remitente) se rellenan cuando están disponibles. Si el destino citado es contenido multimedia descargable, OpenClaw lo guarda mediante el almacén normal de multimedia entrante y expone MediaPath/MediaType para que el agente pueda inspeccionarlo directamente en lugar de ver solo <media:image>.
Los mensajes solo multimedia se normalizan a marcadores de posición: <media:image>, <media:video>, <media:audio>, <media:document>, <media:sticker>.Las notas de voz de grupo autorizadas se transcriben antes del control de mención cuando el cuerpo es solo <media:audio>, de modo que decir la mención del bot en la nota de voz puede activar la respuesta. Si la transcripción aun así no menciona al bot, permanece en el historial de grupo pendiente en lugar del marcador de posición sin procesar.Los cuerpos de ubicación se representan como texto breve de coordenadas. Las etiquetas/comentarios de ubicación y los detalles de contacto/vCard se representan como metadatos no confiables en bloque delimitado, no como texto de prompt en línea.
Los mensajes de grupo no procesados se almacenan en búfer y se inyectan como contexto cuando finalmente se activa el bot.
  • límite predeterminado: 50
  • configuración: channels.whatsapp.historyLimit, reserva messages.groupChat.historyLimit
  • 0 desactiva
Marcadores de inyección: [Chat messages since your last reply - for context] y [Current message - respond to this].
Activadas de forma predeterminada para mensajes entrantes aceptados. Desactivar globalmente:
{ channels: { whatsapp: { sendReadReceipts: false } } }
Anulación por cuenta: channels.whatsapp.accounts.<id>.sendReadReceipts. Los turnos de chat propio omiten confirmaciones de lectura incluso cuando están activadas globalmente.

Entrega, fragmentación y multimedia

  • límite de fragmento predeterminado: channels.whatsapp.textChunkLimit = 4000
  • channels.whatsapp.chunkMode = "length" | "newline"; newline prefiere límites de párrafo (líneas en blanco) y luego recurre a fragmentación segura por longitud
  • admite cargas útiles de imagen, video, audio (nota de voz PTT) y documento
  • el audio se envía como la carga útil audio de Baileys con ptt: true, representándose como una nota de voz push-to-talk; audioAsVoice se conserva en cargas útiles de respuesta para que la salida de nota de voz TTS permanezca en esta ruta independientemente del formato de origen del proveedor
  • el audio Ogg/Opus nativo se envía como audio/ogg; codecs=opus; cualquier otra cosa (incluida la salida MP3/WebM de TTS de Microsoft Edge) se transcodifica con ffmpeg a Ogg/Opus mono de 48 kHz antes de la entrega PTT
  • /tts latest envía la última respuesta del asistente como una nota de voz y suprime envíos repetidos para la misma respuesta; /tts chat on|off|default controla el TTS automático para el chat actual
  • gifPlayback: true en envíos de video habilita la reproducción de GIF animado
  • forceDocument/asDocument enruta imágenes, GIFs y videos salientes mediante la carga útil de documento de Baileys para evitar la compresión multimedia de WhatsApp, conservando el nombre de archivo resuelto y el tipo MIME
  • los pies de foto se aplican al primer elemento multimedia en una respuesta con varios medios, excepto las notas de voz PTT: el audio se envía primero sin pie de foto y luego el pie de foto se envía como un mensaje de texto separado (los clientes de WhatsApp no renderizan de forma consistente los pies de foto de notas de voz)
  • la fuente multimedia puede ser HTTP(S), file:// o una ruta local
  • límite de guardado entrante y límite de envío saliente: channels.whatsapp.mediaMaxMb (predeterminado 50)
  • anulación por cuenta: channels.whatsapp.accounts.<id>.mediaMaxMb
  • las imágenes se optimizan automáticamente (barrido de redimensionamiento/calidad) para ajustarse a los límites salvo que forceDocument/asDocument solicite entrega como documento
  • ante un fallo de envío multimedia, la reserva del primer elemento envía una advertencia de texto en lugar de descartar la respuesta silenciosamente

Citas de respuesta

channels.whatsapp.replyToMode controla las citas de respuesta nativas (las respuestas salientes citan visiblemente el mensaje entrante):
ValorComportamiento
"off" (predeterminado)Nunca citar; enviar como mensaje simple
"first"Citar solo el primer fragmento de respuesta saliente
"all"Citar cada fragmento de respuesta saliente
"batched"Citar respuestas en cola por lotes; dejar sin cita las respuestas inmediatas
Anulación por cuenta: channels.whatsapp.accounts.<id>.replyToMode.
{ channels: { whatsapp: { replyToMode: "first" } } }

Nivel de reacción

channels.whatsapp.reactionLevel controla con qué amplitud el agente usa reacciones emoji:
NivelReacciones de acuseReacciones iniciadas por el agente
"off"NoNo
"ack"No
"minimal" (predeterminado)Sí, guía conservadora
"extensive"Sí, guía fomentada
Anulación por cuenta: channels.whatsapp.accounts.<id>.reactionLevel.
{ channels: { whatsapp: { reactionLevel: "ack" } } }

Reacciones de acuse

channels.whatsapp.ackReaction envía una reacción inmediata al recibir un entrante, controlada por reactionLevel (suprimida cuando es "off"):
{
  channels: {
    whatsapp: {
      ackReaction: {
        emoji: "👀",
        direct: true,
        group: "mentions", // always | mentions | never
      },
    },
  },
}
Notas: se envía inmediatamente después de que se acepta el entrante (antes de la respuesta); si ackReaction está presente sin emoji, WhatsApp usa el emoji de identidad del agente enrutado y recurre a ”👀” (omite ackReaction o define emoji: "" para no enviar acuse); los fallos se registran, pero no bloquean la entrega de la respuesta; el modo de grupo mentions reacciona solo en turnos activados por mención, mientras que la activación de grupo always omite esa comprobación; WhatsApp usa solo channels.whatsapp.ackReaction (messages.ackReaction heredado no se aplica aquí).

Reacciones de estado del ciclo de vida

Define messages.statusReactions.enabled: true para permitir que WhatsApp reemplace la reacción de acuse durante un turno en lugar de dejar un emoji de recibo estático, recorriendo estados como en cola, pensando, actividad de herramienta, Compaction, completado y error:
{
  messages: {
    statusReactions: {
      enabled: true,
      emojis: {
        deploy: "🛫",
        build: "🏗️",
        concierge: "💁",
      },
    },
  },
}
Notas: channels.whatsapp.ackReaction sigue controlando la elegibilidad para mensajes directos y grupos; el estado en cola usa el mismo emoji efectivo que las reacciones de acuse simples; WhatsApp tiene una ranura de reacción de bot por mensaje, por lo que las actualizaciones del ciclo de vida reemplazan la reacción actual in situ; messages.removeAckAfterReply: true borra la reacción de estado final después de la retención configurada para completado/error; las categorías de emoji de herramienta incluyen tool, coding, web, deploy, build y concierge.

Varias cuentas y credenciales

Los ids de cuenta provienen de channels.whatsapp.accounts. La selección de cuenta predeterminada es default si está presente; de lo contrario, el primer id de cuenta configurado (ordenado alfabéticamente). Los ids de cuenta se normalizan internamente para la búsqueda.
  • ruta de autenticación actual: ~/.openclaw/credentials/whatsapp/<accountId>/creds.json (copia de seguridad: creds.json.bak)
  • la autenticación predeterminada heredada en ~/.openclaw/credentials/ todavía se reconoce/migra para flujos de cuenta predeterminada
openclaw channels logout --channel whatsapp [--account <id>] borra el estado de autenticación de WhatsApp para esa cuenta. Cuando se puede acceder a un Gateway, el cierre de sesión detiene primero el listener en vivo para esa cuenta, de modo que la sesión vinculada deja de recibir mensajes antes del próximo reinicio. openclaw channels remove --channel whatsapp también detiene el listener en vivo antes de deshabilitar o eliminar la configuración de la cuenta.En directorios de autenticación heredados, oauth.json se conserva mientras se eliminan los archivos de autenticación de Baileys.

Herramientas, acciones y escrituras de configuración

  • La compatibilidad con herramientas del agente incluye la acción de reacción de WhatsApp (react).
  • Compuertas de acciones: channels.whatsapp.actions.reactions, channels.whatsapp.actions.polls (las acciones existentes tienen true de forma predeterminada), channels.whatsapp.actions.calls (valor predeterminado false, consulta MeowCaller arriba).
  • Las escrituras de configuración iniciadas por el canal están habilitadas de forma predeterminada; deshabilítalas mediante channels.whatsapp.configWrites: false.

Solución de problemas

Síntoma: el estado del canal informa que no está vinculado.
openclaw channels login --channel whatsapp
openclaw channels status
Síntoma: cuenta vinculada con desconexiones o intentos de reconexión repetidos.Las cuentas silenciosas pueden permanecer conectadas más allá del tiempo de espera normal de mensajes; el watchdog reinicia solo cuando se detiene la actividad del transporte de WhatsApp Web, se cierra el socket o la actividad de nivel de aplicación permanece en silencio más allá de la ventana de seguridad más larga (consulta Modelo de tiempo de ejecución arriba).Si los registros muestran repetidamente status=408 Request Time-out Connection was lost, ajusta los tiempos del socket de Baileys en web.whatsapp. Empieza por acortar keepAliveIntervalMs por debajo del tiempo de espera por inactividad de tu red y aumentar connectTimeoutMs en enlaces lentos o con pérdidas:
{
  web: {
    whatsapp: {
      keepAliveIntervalMs: 15000,
      connectTimeoutMs: 60000,
      defaultQueryTimeoutMs: 60000,
    },
  },
}
Corrección:
openclaw channels status --probe
openclaw doctor
openclaw logs --follow
openclaw gateway status
Si el bucle persiste después de corregir la conectividad del host y los tiempos, haz una copia de seguridad del directorio de autenticación de la cuenta y vuelve a vincularla:
cp -a ~/.openclaw/credentials/whatsapp/<accountId> \
  ~/.openclaw/credentials/whatsapp/<accountId>.bak
openclaw channels logout --channel whatsapp --account <accountId>
openclaw channels login --channel whatsapp --account <accountId>
Si ~/.openclaw/logs/whatsapp-health.log dice Gateway inactive, pero openclaw gateway status y openclaw channels status --probe muestran ambos un estado correcto, ejecuta openclaw doctor. En Linux, doctor advierte sobre entradas heredadas de crontab que invocan el script retirado ~/.openclaw/bin/ensure-whatsapp.sh; elimina esas entradas con crontab -e: cron puede carecer del entorno de bus de usuario de systemd y hacer que ese script antiguo informe incorrectamente del estado del Gateway.
Síntoma: openclaw channels login --channel whatsapp falla antes de mostrar un QR utilizable con status=408 Request Time-out o una desconexión de socket TLS.El inicio de sesión de WhatsApp Web usa el entorno de proxy estándar del host del Gateway (HTTPS_PROXY, HTTP_PROXY, variantes en minúsculas, NO_PROXY). Verifica que el proceso del Gateway herede el entorno de proxy y que NO_PROXY no coincida con mmg.whatsapp.net.
Los envíos salientes fallan rápidamente cuando no existe un listener de Gateway activo para la cuenta de destino. Confirma que el Gateway esté en ejecución y que la cuenta esté vinculada.
Las filas de transcripción registran lo que generó el agente; la entrega de WhatsApp se comprueba por separado. OpenClaw solo trata una respuesta automática como enviada después de que Baileys devuelve un id de mensaje saliente para al menos un envío visible de texto o medios.Las reacciones de acuse son recibos previos a la respuesta independientes: una reacción correcta no demuestra que la respuesta posterior de texto/medios haya sido aceptada. Revisa los registros del Gateway para auto-reply delivery failed o auto-reply was not accepted by WhatsApp provider.
Comprueba en este orden: groupPolicy, groupAllowFrom/allowFrom, entradas de lista de permitidos de groups, compuerta de menciones (requireMention + patrones de mención) y claves duplicadas en openclaw.json (las entradas posteriores de JSON5 anulan las anteriores: mantén un único groupPolicy por ámbito).Si channels.whatsapp.groups está presente, WhatsApp aún puede observar mensajes de otros grupos, pero OpenClaw los descarta antes del enrutamiento de sesión. Añade el JID del grupo a channels.whatsapp.groups, o añade groups["*"] para admitir todos los grupos mientras mantienes la autorización de remitentes bajo groupPolicy/groupAllowFrom.
El runtime del Gateway de WhatsApp debe usar Node. Bun se marca como incompatible para el funcionamiento estable del Gateway de WhatsApp/Telegram.

Prompts del sistema

WhatsApp admite prompts del sistema de estilo Telegram para grupos y chats directos mediante los mapas groups y direct. Resolución para mensajes de grupo: primero se determina el mapa groups efectivo; si la cuenta define su propia clave groups, reemplaza por completo el mapa groups raíz (sin fusión profunda). Luego, la búsqueda del prompt se ejecuta en ese único mapa resultante:
  1. Prompt específico de grupo (groups["<groupId>"].systemPrompt): se usa cuando la entrada del grupo existe y su clave systemPrompt está definida. Una cadena vacía ("") suprime el comodín y no aplica ningún prompt.
  2. Prompt comodín de grupo (groups["*"].systemPrompt): se usa cuando la entrada específica del grupo está ausente, o existe sin una clave systemPrompt.
La resolución para mensajes directos sigue el patrón idéntico contra el mapa direct y direct["*"].
dms sigue siendo el contenedor ligero de anulación de historial por DM (dms.<id>.historyLimit). Las anulaciones de prompts viven bajo direct.
Este comportamiento de cuenta-reemplaza-raíz para la resolución de prompts es una anulación superficial simple: cualquier clave groups/direct de cuenta, incluido un objeto vacío explícito, reemplaza el mapa raíz. Difiere de la comprobación de lista de permitidos de pertenencia a grupos descrita arriba, que tiene una red de seguridad de cuenta única para un groups: {} accidentalmente vacío.
Diferencia con Telegram: Telegram suprime groups raíz para cada cuenta en una configuración de varias cuentas (incluso cuentas sin groups propios) para impedir que un bot reciba mensajes de grupo de grupos a los que no pertenece. WhatsApp no aplica esa protección: groups/direct raíz son heredados por cualquier cuenta sin su propia anulación, independientemente del número de cuentas. En una configuración de WhatsApp de varias cuentas, define explícitamente el mapa completo bajo cada cuenta si quieres prompts por cuenta. Comportamiento importante:
  • channels.whatsapp.groups es tanto un mapa de configuración por grupo como la lista de permitidos de grupos a nivel de chat. En el ámbito raíz o de cuenta, groups["*"] significa “todos los grupos son admitidos” para ese ámbito.
  • Añade un systemPrompt comodín solo cuando ya quieras que ese ámbito admita todos los grupos. Para mantener elegible solo un conjunto fijo de IDs de grupo, repite el prompt en cada entrada explícitamente incluida en la lista de permitidos en lugar de usar groups["*"].
  • La admisión de grupos y la autorización de remitentes son comprobaciones separadas. groups["*"] amplía qué grupos llegan al manejo de grupos; no autoriza a todos los remitentes de esos grupos: eso sigue controlado por groupPolicy/groupAllowFrom.
  • channels.whatsapp.direct no tiene un efecto secundario equivalente para DMs: direct["*"] solo proporciona una configuración predeterminada después de que un DM ya haya sido admitido por dmPolicy más reglas de allowFrom o del almacén de emparejamientos.
Ejemplo:
{
  channels: {
    whatsapp: {
      groups: {
        // Use only if all groups should be admitted at the root scope.
        // Applies to all accounts that do not define their own groups map.
        "*": { systemPrompt: "Default prompt for all groups." },
      },
      direct: {
        // Applies to all accounts that do not define their own direct map.
        "*": { systemPrompt: "Default prompt for all direct chats." },
      },
      accounts: {
        work: {
          groups: {
            // This account defines its own groups, so root groups are fully
            // replaced. To keep a wildcard, define "*" explicitly here too.
            "120363406415684625@g.us": {
              requireMention: false,
              systemPrompt: "Focus on project management.",
            },
            // Use only if all groups should be admitted in this account.
            "*": { systemPrompt: "Default prompt for work groups." },
          },
          direct: {
            // This account defines its own direct map, so root direct entries are
            // fully replaced. To keep a wildcard, define "*" explicitly here too.
            "+15551234567": { systemPrompt: "Prompt for a specific work direct chat." },
            "*": { systemPrompt: "Default prompt for work direct chats." },
          },
        },
      },
    },
  },
}

Punteros de referencia de configuración

Referencia principal: Referencia de configuración - WhatsApp
ÁreaCampos
AccesodmPolicy, allowFrom, groupPolicy, groupAllowFrom, groups
EntregatextChunkLimit, chunkMode, mediaMaxMb, sendReadReceipts, ackReaction, reactionLevel
Varias cuentasaccounts.<id>.enabled, accounts.<id>.authDir y otras anulaciones por cuenta
OperacionesconfigWrites, debounceMs, web.enabled, web.heartbeatSeconds, web.reconnect.*, web.whatsapp.*
Comportamiento de sesiónsession.dmScope, historyLimit, dmHistoryLimit, dms.<id>.historyLimit
Promptsgroups.<id>.systemPrompt, groups["*"].systemPrompt, direct.<id>.systemPrompt, direct["*"].systemPrompt

Relacionado