0006 — WhatsApp como Tech Provider, con coexistencia
Estado: aceptada · Fecha: 2026-09-19
Contexto
En septiembre de 2026 Meta verificó a Xenpia como Tech Provider. Desde ese momento los clientes pueden conectar su propio número de WhatsApp desde el panel, con Embedded Signup, sin pasar por el portafolio de Xenpia.
La mayoría de los clientes (consultorios, clínicas) ya atienden por la app WhatsApp Business en un teléfono, con años de conversaciones. Hasta ahora, conectar ese número a la API obligaba a borrarlo de la app y perder el historial, y el manual lo advertía así. Meta ofrece la coexistencia: el número sigue en la app y la Cloud API opera sobre él a la vez.
La auditoría del 2026-09-19 encontró que no había nada de coexistencia, y además:
- El flujo de cliente de Embedded Signup no suscribía la app a la WABA del cliente
(
subscribed_apps): el número quedaba "conectado" sin recibir ningún webhook. - El webhook leía solo
entry[0].changes[0]y solovalue.messages, sin verificarX-Hub-Signature-256. MetaControlleryWhatsAppControllerno tenían ningún guard. Cualquiera que conociera la URL podía canjear codes, enviar desde el número de un cliente o crear números con el token de sistema de Xenpia.- El token de negocio del cliente viajaba al navegador y volvía a una server action.
Plazo externo: Meta depreca Embedded Signup v2 el 15-oct-2026 (otra página suya dice el 8).
Decisión
- Primero cerrar, después abrir. Antes de dar de alta clientes reales:
PermissionGuardcon pertenencia al tenant en todas las rutas de WhatsApp/Meta que no son el webhook;- firma
X-Hub-Signature-256obligatoria en los dos webhooks (META_WEBHOOK_SIGNATURE); send-textexige una cuenta de canal del tenant; no se envía con el número global;- ningún log imprime tokens.
- Onboarding entero en el backend (
POST /api/whatsapp/onboarding/complete): canje del code,subscribed_apps,/registero sincronización, y guardado. El token no sale del servidor. - Embedded Signup v4 con
featureType: 'whatsapp_business_app_onboarding'en el wizard de WhatsApp. Una sola ventana ofrece las dos opciones y el evento de Meta decide el modo:FINISH→cloud_api(con/register),FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING→coexistence(sin/register). - Un eco del teléfono pausa el bot. Si el negocio contesta desde la app
(
smb_message_echoes), es una persona atendiendo: se guarda como salientesource = 'phone'y se pausa el bot en esa conversación, con el mismo mecanismo que el traspaso a humano (status_bot = 'paused', motivo__phone__). Si ya estaba en pausa, no se pisa el motivo anterior. - Se importan contactos e historial. Se pide
smb_app_data(contactos y luego historial) al conectar. El historial entra consource = 'history'y su hora original, sin invocar ni pausar al bot, y no cuenta como no leído. La agenda crea los contactos que falten pero nunca renombra, funde ni borra fichas existentes (mismo criterio que la identidad al agendar por bot). - La desconexión desde la app (
account_update/PARTNER_REMOVED) marca el canal como desconectado y pausa su bot. No se intenta reconectar solo.
Alternativas descartadas
- Solo registrar el eco, sin pausar el bot. El bot y la persona contestarían a la vez al mismo cliente, y en una clínica eso puede terminar en dos citas distintas.
- Solo contactos, sin historial. Más simple, pero la bandeja arrancaría vacía justo con los clientes que más contexto tienen. Meta solo permite pedir el historial en las primeras 24 horas, así que no se puede dejar para después.
- Coexistencia con una ventana y un
config_idaparte. Duplica configuración en Meta y en.env. La v4 ofrece las dos opciones en la misma ventana y el evento de vuelta ya dice cuál eligió el cliente.
Consecuencias
- Una migración,
20261012000001: añade el estado de la coexistencia awhatsapp_oauth_credentialsymessages.source. El panel lee esas columnas con respaldo si la migración aún no está aplicada. - En el App Dashboard hay que suscribir
history,smb_app_state_sync,smb_message_echoesyaccount_update, además demessages. Si falta alguno, no hay error: simplemente no llega. - Los límites de Meta pasan a ser límites del producto y el manual los explica: 20 mensajes/s por número, sin grupos, difusión, mensajes temporales, ver una vez ni llamadas por la API, y WhatsApp para Windows/WearOS desvinculados.
- Queda como deuda: el token del cliente sigue sin cifrar en reposo, y las rutas
xenpia-*no atan cada número del portafolio a un tenant.
Detalle técnico: WhatsApp §3.b y §3.c.