Cambios por versión
Esta página sigue la versión del contrato /v1: la que publica el OpenAPI (info.version) y
GET /v1/capabilities en contrato. Hoy hay una sola, la v1.0.0. El binario del motor tiene
su propia versión y su commit (los muestra motorctl, la página Operación del panel y la métrica
motor_info): no confundas una con la otra.
v1.0.0 (24/10/2026)
Sección titulada «v1.0.0 (24/10/2026)»La primera versión del contrato. Cubre las funciones de transporte del plan MW0 a MW12 (las que WhatsApp permite a un dispositivo vinculado y el SDK soporta), con entrega durable de los eventos. Se probó con el driver falso, con integración sobre Postgres y MinIO reales, y a mano con números reales en iOS y Android entre el 17 y el 23 de octubre de 2026 (109 casos: 80 pasan, 20 parciales, 5 sin ejecutar y 4 que no se pueden hacer). Lo parcial está en Limitaciones de WhatsApp.
Para migrar: nada. Es la primera versión.
Instancias y vinculación
Sección titulada «Instancias y vinculación»- Varias instancias (números) por app, con un cupo por app que se respeta de forma atómica (
429al pasarlo). Una key restringida a instancias no puede crear otras. - Vinculación por QR y por código (el teléfono en formato internacional, con
+). La passkey está expuesta como experimental: el motor devuelve la acción real que pide WhatsApp. - Cada intento sube la generación de la instancia;
generacion_esperadaevita mandar por un número que se volvió a vincular. - Estado deseado y estado observado por separado, con motivos tipificados. Reconexión automática con espera creciente; pausar, reanudar, desconectar, desvincular (local o avisando a WhatsApp) y archivar sin borrar el ledger.
- Diagnóstico sin datos personales (
GET /v1/instances/{id}/diagnostics). - Proxy por instancia (HTTP con CONNECT o SOCKS5), elegido de una lista que administra el
operador; nunca una URL suelta ni las variables
HTTP_PROXYdel entorno. - Un solo proceso es dueño de cada sesión, con fencing en la base: una sesión nunca queda abierta en dos procesos.
Mensajes
Sección titulada «Mensajes»- Un solo endpoint para mandar:
POST /v1/instances/{id}/messages, conIdempotency-Keyobligatoria. Responde 202 con el comando guardado; el resultado llega como evento. - Tipos:
text(con cita, menciones y vista previa de enlaces),location,contacts,reaction,edit(hasta 20 minutos),revoke(borrar para todos),forward,disappearing(temporizador del chat) ypoll(encuestas, con el voto vigente de cada votante y el resultado agregado). - Outbox con ID de proveedor fijo: un reintento nunca duplica. Un envío que pudo salir o no queda
ambiguoy nunca se reenvía solo; si su eco o su recibo aparecen, se concilia. - Recibos por destinatario (
entregado,leido,reproducido), marcar como leído o reproducido, presencia propia y suscripción a la presencia de un contacto. - Mensajes temporales: el motor respeta el vencimiento, borra el contenido y emite
message.expired. - Lo que el motor no reconoce llega como
no_soportadocon el tipo del proveedor, nunca con un texto inventado. - Consulta del store propio:
GET /v1/instances/{id}/messages, con filtros, cursor y el detalle de los recibos.
Multimedia
Sección titulada «Multimedia»- Almacenamiento privado en un bucket compatible con S3 (MinIO): MIME real, tamaño, digest, cuota por app y por instancia, y retención.
- Envío de
image,video,documentyaudiopormedia_ido por URL (con lista de hosts permitidos y protección contra SSRF). - Subtipos con conversión acotada (ffmpeg): nota de voz (
ptt), vídeo circular (ptv),sticker(estático y animado) ygif. - Recepción con descarga durable (
media.readyomedia.failed) y lectura en streaming, también en base64 acotado.
Interactivos
Sección titulada «Interactivos»tipo: interactivecon botones (reply, copiar código, URL, llamada y PIX), lista nativa (con respaldo de texto opcional) y carrusel de 2 a 10 tarjetas.- Las respuestas llegan como
interactive.response, correlacionadas con el comando que mandó el interactivo; lo que no se puede leer, comointeractive.unknown. - Todos llevan el aviso «no garantizado en números no oficiales»: el motor informa el resultado real y nunca da por visto lo que WhatsApp puede no mostrar.
Grupos y comunidades
Sección titulada «Grupos y comunidades»- Crear, salir, cambiar nombre, descripción, foto y ajustes (anuncios, bloqueo, aprobación, modo de incorporación), administrar participantes y solicitudes, obtener o revocar la invitación, consultarla sin entrar, unirse y mandarla por mensaje.
- Comunidades: crear, vincular y desvincular grupos (un vínculo por par, versionado) y listar sus grupos y participantes.
- Lo que cambia desde un teléfono llega como
group.changed,group.participants,group.joinedycommunity.changed.
Canales
Sección titulada «Canales»- Crear, seguir y dejar de seguir (por JID o enlace), silenciar, consultar una invitación,
listar publicaciones por
server_id, reaccionar, marcar vistas y consultar los contadores. - Publicar en un canal es un envío más, con destino
…@newsletter.
Estados
Sección titulada «Estados»- Publicar estados de texto, imagen o vídeo por el outbox, con la audiencia explícita (experimental).
- Recibir los estados de los contactos con su retención: pasado el plazo, el contenido se borra y el evento queda redactado.
Llamadas
Sección titulada «Llamadas»- Eventos de cada señal de una llamada entrante (
call.*), rechazo a pedido y rechazo automático por instancia, con un texto opcional a quien llamó. - Atender y hacer llamadas no está soportado: responde 501
llamadas_no_soportadas, nunca un éxito simulado.
Perfil y privacidad
Sección titulada «Perfil y privacidad»- Nombre, Info (recado) y foto propios; las diez opciones de privacidad del SDK y el temporizador por defecto de los mensajes temporales; bloquear, desbloquear y listar bloqueados.
- Cada dato del perfil dice quién lo fijó (
whatsapp,motoroconsulta) y si WhatsApp lo confirmó.
Contactos
Sección titulada «Contactos»- Los contactos de transporte del número (libreta, push name, nombre de negocio, Info y foto), sincronizados con el teléfono. No son contactos de tu CRM.
- Identidades PN y LID comprobadas por instancia, con un
peer_refestable. - Consultas con cuota diaria por app: si un número está en WhatsApp, el perfil público y la foto.
Etiquetas y Business
Sección titulada «Etiquetas y Business»- Estado de la app de WhatsApp: fijar (hasta 3 chats), archivar, silenciar, leído y no leído, destacar y borrar el chat, como comandos durables sincronizados con el teléfono. El candado que pone el teléfono también se refleja.
- Etiquetas de WhatsApp Business: crear, editar, eliminar y poner o quitar en chats y mensajes.
- Perfil comercial de un número (
GET /v1/instances/{id}/business/profile).
Catálogo
Sección titulada «Catálogo»- Lectura del catálogo de productos y de las colecciones de un negocio, con cursor, caché y su cuota. Solo lectura: nada de crear productos, pedidos ni inventario.
- Cada consulta remota deja
query.completedoquery.failed.
Historial
Sección titulada «Historial»- El protocolo del historial se procesa siempre; importar el contenido es opcional por instancia (apagado de fábrica), con tope de días y de mensajes.
- Trabajos de historial (el inicial, el pedido de un chat y la importación hacia atrás), con progreso, cancelación y su resultado. WhatsApp no garantiza la historia completa, y el motor nunca lo afirma.
Eventos y entrega
Sección titulada «Eventos y entrega»- Ledger por instancia: cada hecho se guarda con un
seqque crece sin huecos, en la misma transacción que lo acepta. El sobre de los eventos es el mismo por cualquier canal (schema_version: 1); el catálogo tiene los 75 tipos. - Lectura paginada:
GET /v1/instances/{id}/events. - Webhooks firmados con HMAC-SHA256 (firma
v1), rotación del secreto sin cortar entregas, orden por instancia, reintentos durante 72 horas, entregas fallidas visibles y replay que nunca vuelve a mandar nada a WhatsApp. - Streams SSE y WebSocket leídos del ledger, con cursor firmado y reanudación sin saltos ni repetidos.
- Brokers: NATS, RabbitMQ, Kafka y SQS, configurados por el operador (no hay ruta en
/v1). Pusher, S3 y los bots se integran por guía, sin adaptador propio. - SDK de Go (
sdk/go): el cliente de/v1, el verificador y el receptor de webhooks, un inbox de ejemplo que deduplica porevent_id, el cliente de streams y dobles para tus pruebas.
Operación
Sección titulada «Operación»- Backup diario cifrado (AES-256-GCM, archivo
.mwbk), con el store de sesiones incluido, verificación al terminar y retención; RPO de 24 horas y RTO de 1 hora. El restore es aislado y arranca sin abrir sesiones hasta que el operador confirma el corte del original. - Actualizaciones: cada migración declara si permite volver atrás;
migrate verificarrevisa antes de actualizar; canario por instancias marcadas. Volver atrás de un cambio incompatible, o del store de whatsmeow, es restaurar un backup. - Métricas en formato Prometheus (
/admin/api/metricas), watchdog de instancias trabadas (avisa, nunca reinicia) y límites del proceso desde Ajustes. - Panel de operadores servido por el propio motor en
/admin: apps y keys, instancias y sus fichas, entregas, backups, operación y Ajustes; exportación de la configuración saneada. - Capacidad medida con carga sintética y el driver falso (hasta 10 instancias): no afirma el rendimiento con WhatsApp real.
Seguridad
Sección titulada «Seguridad»- El motor no se expone a internet. Dos identidades que no se mezclan: operadores con sesión,
cookie y CSRF en
/admin/api, y apps con API keys (Bearer mw_…) con scopes en/v1. - Las API keys se guardan con hash, se muestran una sola vez y rotar revoca la anterior al instante. Una app solo ve lo suyo: un recurso ajeno es 404, sin enumerar dueños.
- El entorno solo lleva cuatro variables (
DATABASE_URL,APP_MASTER_KEY,APP_ENV,PORT); el resto va en Ajustes, con los secretos sellados. Los payloads del ledger también van sellados. - Logs y auditoría sin secretos, teléfonos, JIDs ni contenido. Webhooks, brokers, proxies y descargas por URL pasan por una lista de destinos permitidos con protección contra SSRF.
- Contenedor sin root y de solo lectura; el motor nunca reinicia una instancia en bucle. Secret scan, SBOM y licencias revisados.
Limitaciones conocidas
Sección titulada «Limitaciones conocidas»Lo que la v1.0.0 no resuelve, sea por WhatsApp o por el motor:
- Las listas nativas no se dibujan en iOS: usa el respaldo de texto (detalle).
- De una ubicación en tiempo real solo llega el primer mensaje (detalle).
- La presencia no trae el «en línea» real ni la última vez (detalle).
- Las vistas y reacciones de un canal no llegan en vivo: se consultan (detalle).
- La Info propia cambiada desde el teléfono no produce
profile.updated(detalle). - El historial bajo pedido depende de que el teléfono conteste (detalle).
- Sin soporte: llamadas de voz o vídeo, listas de difusión y plantillas de la Cloud API.
- Las colecciones del catálogo dan solo la primera vista que entrega WhatsApp: la continuación remota es una brecha conocida.
- Los brokers se probaron en contenedores locales; contra AWS SQS real y un broker gestionado, todavía no (casos B5 y B6).
APP_MASTER_KEYno se puede rotar en el lugar en esta versión.
La lista completa, con fechas y qué hacer, está en Limitaciones de WhatsApp.
Compatibilidad del contrato
Sección titulada «Compatibilidad del contrato»Se considera un cambio incompatible, y no se hace dentro de /v1 ni de un mismo
schema_version:
- Quitar o renombrar una ruta, un parámetro, un campo de una respuesta o del sobre, un tipo de
evento, un campo de
data, un scope o unerror_code. - Cambiar el tipo o el significado de un campo, o un código HTTP de una respuesta documentada.
- Hacer obligatorio un campo de pedido que era opcional, o achicar un límite documentado.
- Cambiar la firma de los webhooks o las garantías de entrega: el orden por instancia, el
seqsin huecos y la entrega al menos una vez.
No se considera incompatible, y puede llegar en una versión menor de /v1:
- Rutas, scopes y tipos de evento nuevos.
- Campos opcionales nuevos en un pedido.
- Campos nuevos en una respuesta, en el sobre o en
data. - Valores nuevos en una enumeración: estados, motivos,
error_code,contenido.tipo, acciones.
Para que una versión menor no rompa tu integración:
- Ignora los campos que no conoces y no falles con un tipo de evento nuevo: guárdalo o descártalo.
- Trata un valor desconocido de una enumeración como «otro», no como un error.
- No mandes campos de una versión más nueva a un motor más viejo: los cuerpos son cerrados y responden 422.
- Revisa
GET /v1/capabilitiesal arrancar para saber qué ofrece el motor que tienes.
Un cambio incompatible en los eventos llegaría con un schema_version nuevo, y uno en la API, con
otro prefijo de ruta. Esta página va a decir, por versión, qué cambió y cómo migrar.