10 días en lo invisible: construir un sistema de correos electrónicos que soporte la producción
10 días. 30 commits. Cero nuevas características visibles. Mientras los usuarios de TAMSIV esperaban un nuevo botón, un nuevo color o una nueva función de voz, pasé cada día reescribiendo algo que nadie verá jamás: la forma en que la aplicación se comunica con las personas por correo electrónico. Y me di cuenta de que lo invisible quizás representa el 80% de lo que hace que un producto permanezca instalado.
Puntos clave a recordar
- Un sistema completo de correos electrónicos transaccionales: recordatorio de verificación, bucle de retroalimentación D+7, preferencias, cancelación de suscripción limpia, historial por usuario.
- Un webhook de Resend que escucha los 6 tipos de eventos (delivered, opened, clicked, bounced, complained, delivery_delayed) para agregar métricas en tiempo real.
- Un flujo anti-suplantación de identidad RGPD: un usuario puede reportar un correo electrónico de bienvenida que nunca solicitó, eliminamos la suscripción inmediatamente.
- Un cron diario que envía un recordatorio único a las cuentas no verificadas desde hace 3 días, i18n en 6 idiomas.
- Dos hotfix móviles en paralelo (v1.07 + v1.08): auditoría gesture-handler, rediseño de la UI del feed, modales estables, 8 errores corregidos.
¿Por qué construir un sistema de correos electrónicos completo cuando Supabase Auth ya envía los correos de verificación?
Es una pregunta legítima. Supabase, en su configuración predeterminada, ya envía un correo electrónico de verificación en cada registro. Muchos productos se detienen ahí y no añaden nada más. Eso es suficiente para validar un correo electrónico, no para construir una relación con el usuario.
En producción desde el 4 de abril, me encontré con docenas de cuentas creadas pero no verificadas. Sin seguimiento. Sin retroalimentación en D+7 para saber si la aplicación les era útil. Sin una forma limpia para que alguien dijera "este correo electrónico no era para mí". Y, sobre todo, ninguna visibilidad por parte del administrador sobre lo que se enviaba, llegaba, rebotaba o se marcaba como spam.
Un sistema de correo electrónico transaccional propio es exactamente lo que separa un producto "técnicamente funcional" de un producto que parece serio. Las grandes aplicaciones lo ocultan detrás de su pulido. Las pequeñas aplicaciones lo descuidan y pierden usuarios sin entender por qué.
El cron diario que envía un recordatorio, y SOLO UNO
El primer ladrillo colocado es un cron diario que se ejecuta a una hora fija en Vercel. Consulta la base de datos, selecciona las cuentas creadas hace exactamente 3 días y aún no verificadas, y activa un recordatorio único por usuario.
[EXPERIENCIA PERSONAL] La regla "un solo recordatorio" es voluntaria. He recibido más correos electrónicos de recordatorio abusivos de los que puedo contar. Tres recordatorios en 48 horas, cinco en una semana, diez en un mes. Es la mejor manera de hacer que la gente se dé de baja incluso antes de que haya probado el producto.
El cron escribe en una tabla dedicada para saber quién recibió qué y cuándo. Si un usuario ya ha sido contactado, se omite. Si el envío de Resend falla, el error se registra, pero el cron no lo reintenta automáticamente: prefiero la visibilidad de los fallos a una saturación silenciosa.
El bucle de retroalimentación en D+7: 4 botones, 4 verdades
[INSIGHT ÚNICO] Siete días después del registro, cada nuevo usuario recibe un correo electrónico con 4 botones clicables: me encanta, lo odio, tengo una sugerencia, encontré un error. Cada botón apunta a una página dedicada que registra la respuesta y ofrece un área de comentarios libre.
¿Por qué esta forma precisa? Porque una calificación de Google Play es filtrada por quien tiene más energía para dejarla. Un correo electrónico con 4 botones captura la verdad silenciosa. La de las personas que nunca irán a la tienda a escribir un comentario, pero que con un clic pueden decir "no me gustó".
Técnicamente, cada botón lleva un token firmado relacionado con el usuario y el tipo de retroalimentación. La ruta GET en el sitio valida el token, registra la respuesta con el user_id y el tipo, y muestra la página correspondiente con un formulario libre. No se requiere autenticación adicional para dejar un comentario: es voluntario. La fricción mata la retroalimentación.
El flujo anti-suplantación: RGPD sin drama
Caso concreto: alguien crea una cuenta TAMSIV con el correo electrónico alice@exemple.fr. Pero Alice no ha solicitado nada. Recibe un correo electrónico de bienvenida para una cuenta que nunca creó. ¿Qué hace?
Sin este flujo, ella guarda el correo electrónico en el spam o reporta al remitente. En ambos casos, es una pérdida: pérdida para mí en cuanto a la reputación de envío, pérdida para ella que no tiene una forma limpia de cerrar el tema, pérdida para la persona que cometió el error tipográfico y que nunca más recibirá comunicación en la dirección correcta.
En la nueva versión, cada correo electrónico de bienvenida contiene un enlace "no creé esta cuenta". Clic, página dedicada, confirmación. La suscripción se elimina inmediatamente de la base de datos, se almacena un registro para auditoría y un mensaje final confirma a Alice que su dirección ya no se utilizará. RGPD sin drama. Una acción, tres segundos, listo.
[DATOS ORIGINALES] La página está traducida a los 6 idiomas de la aplicación (francés, inglés, alemán, español, italiano, portugués). Los caracteres acentuados estaban rotos en algunos idiomas debido a un problema de codificación en el archivo de traducción: corregido en el commit 12e61a7.
El webhook de Resend: saber antes de que el usuario escriba
Todos los envíos pasan por Resend, un proveedor de correos electrónicos transaccionales centrado en la experiencia del desarrollador. Resend ofrece webhooks para cada evento del ciclo de vida de un correo electrónico: email.sent, email.delivered, email.opened, email.clicked, email.bounced, email.complained, email.delivery_delayed.
Todos son escuchados y almacenados en una tabla dedicada con la referencia al usuario, el tipo de correo electrónico, la marca de tiempo y los metadatos asociados. Esto permite luego agregar: cuántos correos electrónicos llegaron hoy, cuántos se abrieron, cuáles generaron un clic, cuántos rebotaron.
El panel de administración muestra estas estadísticas en tiempo real con insignias que se incrementan con cada envío. ¿Un rebote que aparece en un usuario? Lo veo inmediatamente, puedo verificar si es un problema temporal o un correo electrónico muerto, y ajustar. ¿Una queja (reporte como spam)? Prioridad alta, investigación inmediata.
El historial de envíos agrupado por usuario, con reenvío y deduplicación
El administrador ve todos los envíos recientes, pero agrupados por usuario. Si Alice recibió su correo electrónico de verificación, luego su recordatorio D+3 y luego su retroalimentación D+7, veo una línea "Alice" con tres sublíneas en expansión. Más legible que un flujo cronológico bruto.
Cada envío tiene una insignia de "fuente" (automático / manual). Los envíos automáticos (cron, triggers) se diferencian de los envíos manuales (botón "reenviar" en el administrador). Existe un botón "reenviar" en cada línea para los casos en que un correo electrónico llegó a spam, para forzar un reintento en otro alias o para probar un cambio de plantilla.
Un sistema de deduplicación evita el re-spam: si envié un correo electrónico de bienvenida hace 2 horas y quiero hacer un envío grupal "todas las inscripciones del día", Alice se excluye automáticamente porque ya lo recibió. Una insignia "elegible" se muestra en cada usuario para indicar si puede recibir el envío actual o no.
Dos hotfix móviles en paralelo: v1.07 y v1.08
Mientras el backend de correo electrónico avanzaba en Vercel, la aplicación móvil necesitaba aire. Se lanzaron dos versiones en la Play Store en Alpha, y luego en producción.
v1.07 (7d65fd0): 8 errores específicos, además de una nueva vista compacta en las carpetas. Cuando tienes 15 subcarpetas en un proyecto, quieres ver la estructura de un vistazo sin desplazarte. Un interruptor alterna entre la vista detallada (tarjetas completas con miniaturas y vistas previas) y la vista compacta (líneas densas con solo el nombre y el recuento). Cero características nuevas en apariencia, pero un cambio de uso para los usuarios intensivos.
v1.08 (a494e7e): auditoría completa de gesture-handler. TAMSIV utiliza react-native-gesture-handler en todas partes, pero varias pantallas todavía tenían TouchableOpacity o FlatList importados directamente de react-native. Resultado: toques intermitentes que no pasaban, imposibles de reproducir, reportados por usuarios que terminaban desinstalando sin entender.
La auditoría afectó a más de 25 archivos. Cada TouchableOpacity, cada FlatList, cada ScrollView se cambió a la importación de gesture-handler. El error de los toques fantasma está corregido. Aprovecha para rediseñar la UI del feed y estabilizar todos los modales: de paso, el pulido da un salto notable.
Lo que aprendí después de 10 días
Lo que me impactó fue que cada línea de código escrita durante estos 10 días permanecerá invisible mientras funcione. Nadie dice "guau, tu correo electrónico de verificación llegó justo a tiempo, una sola vez, con el idioma correcto". Nadie nota que se detectó un rebote automáticamente y que el administrador vio la señal incluso antes de que el usuario escribiera.
Lo invisible solo se nota cuando falla. Y en ese momento, el usuario no entiende lo que se rompió: solo entiende que la aplicación "no funciona bien". Desinstala. No escribe. No dice por qué.
Así que lo invisible es quizás el 80% de lo que hace un producto terminado. No los botones que se añaden en las versiones de características, no los colores que se rehacen, no las animaciones. Solo el hecho de que cuando algo tiene que llegar al usuario, llega. En el momento adecuado. Una vez. En el idioma correcto.
No es sexy para publicar en LinkedIn. Pero es lo que hace que un producto permanezca instalado.
Preguntas frecuentes
¿Por qué no usar un servicio todo en uno como Mailchimp o ConvertKit?
Porque son herramientas de newsletter, no transaccionales. Están diseñados para envíos grupales editoriales, no para triggers individuales vinculados al ciclo de vida del usuario. Resend está diseñado para correos electrónicos transaccionales (bienvenida, verificación, restablecimiento de contraseña, bucle de retroalimentación). Es la herramienta adecuada para el trabajo adecuado.
¿Es fiable el webhook de Resend para los eventos?
Muy fiable. Resend reintenta los webhooks en caso de fallo, firma las cargas útiles para la autenticación y expone una consola para reproducirlos manualmente. En 10 días de uso, no he detectado ni un solo evento perdido.
¿Cómo gestionar las traducciones de los 6 idiomas para los correos electrónicos?
Cada plantilla de correo electrónico se define en francés, luego se traduce mediante un script automático a través de OpenRouter (LLM). Las claves de traducción residen en los mismos archivos messages/*.json que el sitio, con un espacio de nombres emails dedicado. El envío del lado del servidor toma el idioma preferido del usuario almacenado en la base de datos, con un fallback a inglés si no está presente.
¿Se abusa del flujo anti-suplantación? ¿Se puede eliminar la cuenta de otra persona?
No. El enlace "no creé esta cuenta" está firmado con un token vinculado a la dirección de correo electrónico de destino. Solo la persona que recibe el correo electrónico puede hacer clic en él y eliminar la suscripción. Si alguien más intenta llamar a la ruta sin el token correcto, devuelve un error 403 sin hacer nada.
¿Este sistema se aplicará a iOS cuando se lance la aplicación?
Sí, es backend. Los correos electrónicos se envían desde Vercel y no dependen de la plataforma de la aplicación móvil. El día que TAMSIV se lance en iOS (12 personas ya han hecho clic en "Descargar para iOS" en el sitio), el flujo de correos electrónicos será idéntico.