Punto de partida: el login por WhatsApp no es el login diario — es el ALTA de un dispositivo (y la recuperación de la cuenta). El OTP inverso verifica el número (el ancla de la cuenta) y autoriza a ese equipo; acto seguido el dispositivo crea su passkey, que desde entonces es su credencial: login y renovación del token sin tocar WhatsApp. Si un dispositivo no crea passkey, puede seguir entrando por WhatsApp cada vez — soportado, pero tu app debe crear la passkey inmediatamente tras la verificación. Esta página cubre el ciclo completo (usuario, dispositivos, sesiones, renovación, revocación y recuperación) y es también su laboratorio: cada paso ejecuta llamadas reales al sandbox.
code (lo que enviarás por WhatsApp), sessionId (secreto de la sesión: sirve para el status y como credencial del push MQTT) y waLink (abre el chat listo)
// la respuesta real aparecerá aquí
// ejecuta el paso 1 — la espera arranca sola (push MQTT, con polling de fallback)
// tus claims aparecerán aquí
WhatsApp se usa una sola vez. Con el JWT de la verificación, el dispositivo registra una passkey (WebAuthn: biometría del sistema, sin contraseñas). Desde entonces, entrar — y renovar el token cada hora — es una firma silenciosa: Motify no almacena refresh tokens que robar ni revocar.
// completa los pasos 1-2 primero
// firma un desafío y recibe un JWT nuevo — sin WhatsApp, sin contraseña
Son tres cosas distintas con ciclos de vida distintos — y cada una tiene su flujo:
| Nivel | Qué es | Alta | Baja | Vida |
|---|---|---|---|---|
| Usuario | La cuenta, anclada al número de teléfono | Verificación por WhatsApp (pasos 1-2) — una vez por cuenta (y en cada recuperación) | Solo por soporte/bloqueo | Permanente |
| Dispositivo | Cada equipo autorizado a entrar (su passkey) | Enrolamiento del dispositivo (paso 4). El primero pasa con el JWT del enroll; cada
dispositivo adicional exige verificación por WhatsApp fresca (≤10 min) — si no,
409 requiere_verificacion |
DELETE /passkey/credentials/{id} — revocarlo |
Hasta revocarlo |
| Sesión | El JWT en memoria de la app | Login con passkey (paso 5) | Descartar el token (ver abajo) | 1 hora |
PASSKEY_MAX_DEVICES; al
alcanzarla, register/start responde 409 limite_dispositivos y la app ofrece
revocar uno). Matiz importante: una passkey sincronizada (iCloud/Google) vive en todos los
equipos del usuario a la vez — el límite cuenta credenciales, no aparatos físicos. Hoy: sin límite.
register/start exige un sello de verificación fresca (≤10 min desde una verificación por
WhatsApp confirmada). El flujo de la app: recibir 409 requiere_verificacion → correr el
la verificación (pasos 1-2, donde el dueño ve QUÉ dispositivo pide entrar y consiente con el botón) → reintentar
el registro. Toda alta de dispositivo, la primera y las siguientes, pasa por el WhatsApp del dueño.
deviceId que tu app persiste
y envía en verify/start): cuenta nueva → "Bienvenido a Motify"; dispositivo
habitual (deviceId ya enrolado) → "Tu dispositivo habitual está iniciando sesión"; dispositivo
desconocido → "Nuevo dispositivo intenta entrar a tu cuenta" (tono de alerta). Y una cuenta
dormida (sin actividad de dispositivos por más de 60 días) recibe el MISMO mensaje que una cuenta
nueva — no se filtra que existía — y al confirmar arranca perfil limpio: la cuenta anterior se
archiva (recuperable solo por soporte). Es la defensa contra números prepago reciclados. El
deviceId es auto-reportado: afina el mensaje; la seguridad sigue siendo el tap del dueño.
Recuperación de cuenta = la misma verificación: teléfono nuevo o
todos los dispositivos revocados → verificar el número por WhatsApp devuelve el mismo
userId (el número es el ancla) y habilita enrolar el equipo nuevo. No existen cuentas
huérfanas ni "olvidé mi contraseña": no hay contraseñas.
Motify no emite refresh tokens, a propósito: un refresh token es un secreto de larga vida que hay que almacenar, rotar y poder revocar — y que se puede robar. Aquí ese rol lo cumple la passkey: cuando el JWT expira (1 hora), la app firma un desafío en silencio y recibe un token nuevo. Mismo resultado que un refresh, sin ningún secreto persistido en el servidor.
El patrón que tu app debe implementar es un envoltorio de red con reintento único:
async function motifyFetch(url, init = {}) {
const call = () => fetch(url, { ...init,
headers: { ...init.headers, authorization: "Bearer " + session.token } });
let res = await call();
if (res.status === 401) { // token vencido (o revocado)
session.token = await passkeyLogin(); // login/start → firma → login/finish
res = await call(); // reintenta UNA vez
}
return res;
}
passkeyLogin() falla → la app cae a la verificación por
WhatsApp, que re-vincula la misma cuenta (el número es el ancla). Nunca hay una sesión
"muerta sin salida".Puedes ver este ciclo en vivo arriba: deja vencer el token del paso 2 (1 hora) y ejecuta el paso 5 — o córrelo de inmediato: el JWT que devuelve reemplaza al anterior, que es exactamente lo que hará tu app cada hora de uso.
| Regla | Detalle |
|---|---|
| Estados del polling | PENDING (espera el envío) → AWAITING_CONFIRM (muestra "confirma en tu WhatsApp") → VERIFIED (token en la misma respuesta) · DENIED (403: aborta y no reintentes solo). |
| Sesión | Expira a los 10 min → 404. Reinicia con verify/start. |
| Espera del estado | Suscríbete por MQTT a motify/verify/{sessionId} (credencial: verify.<sessionId> en el authorizer) — los estados llegan por push al instante. Mantén un polling de fallback cada 10-15 s por si la conexión falla; nunca más rápido que 2 s. |
| Vida del token | 1 hora. Ante 401 en cualquier API: re-autentica y reintenta UNA vez. |
| Identidad | userId es estable por número: re-verificar devuelve el MISMO userId. Guarda el userId, no el teléfono. |
| Dispositivo | platform/model son contexto UX del mensaje de confirmación — envíalos siempre (mejora la decisión del usuario), pero no son un control de seguridad. |
| Renovación del token | Sin refresh tokens: ante 401, repite passkey/login en silencio (una firma) y reintenta. Solo si el dispositivo perdió su passkey se vuelve a la verificación por WhatsApp. |
| Passkey | Regístrala INMEDIATAMENTE tras la verificación (paso 4), de forma transparente para el usuario. RP ID: motify.illari.ai. |
| Dispositivo adicional | register/start → 409 requiere_verificacion: corre la verificación por WhatsApp de nuevo y reintenta en ≤10 min. 409 limite_dispositivos: ofrece revocar uno (lista + DELETE). |
| Recuperación | Teléfono/equipo nuevo = misma verificación por WhatsApp → mismo userId. Tras recuperar, ofrece al usuario revisar y revocar sus dispositivos viejos. |