Dev Life

Creación de flujos de trabajo de emails transaccionales para restablecer contraseñas con la API de Mailgun

¿Tienes problemas para enviar emails seguros para restablecer contraseñas? Esta guía práctica te explica paso a paso cómo crear un flujo de trabajo de emails fiable y automatizado con la API de Mailgun y FastAPI.…
Imagen para Creación de flujos de trabajo de emails transaccionales para restablecer contraseñas con la API de Mailgun

Emails transaccionales son emails automatizados activados por acciones de los usuarios en un sitio web, una aplicación o un servicio. A diferencia de emails de marketing, que promocionan productos o servicios, los emails transaccionales ofrecen información fundamental, a menudo en tiempo real.

Los flujos de trabajo de emails transaccionales van un paso más allá y automatizan una secuencia de emails a partir de las acciones de los usuarios para garantizar una comunicación oportuna y una buena experiencia de usuario. Las empresas confían en servicios como Mailgun para sus emails transaccionales porque son rápidos, relevantes y se activan mediante acciones inmediatas de los usuarios. Por ejemplo, el flujo de trabajo permite a los usuarios restablecer rápidamente su contraseña y recuperar el acceso a su cuenta.

En este tutorial, descubrirás cómo crear un flujo de trabajo de emails transaccionales para restablecer contraseñas con la API de Mailgun.

Configurar una cuenta de Mailgun

Si aún no tienes una cuenta de Mailgun, lo primero que debes hacer es crear una. Asegúrate de revisar el email asociado a tu cuenta de Mailgun y de verificar tu cuenta:

Account Verification Image

A continuación, introduce tu número de teléfono y el código de autorización proporcionado para activar tu cuenta:

Account Verification Phone Number image

Una vez configurada tu cuenta de Mailgun, llega el momento de obtener una clave de API para empezar a utilizar el servicio.

Obtención de una clave de API

Para que tu aplicación interactúe con los servicios de Mailgun, es necesario autenticarla a través de una clave de API. Para conseguir tu clave de API, ve al panel de control de Mailgun y selecciona Get started a la izquierda para consultar la guía Get started guide:

API Get Started Guide Image

Haz clic en el botón Get started que aparece bajo Create an API Key. A continuación, incluye una descripción que refleje bien el propósito de la clave de la API. Por ejemplo, si la clave de API es para procesos de autenticación de usuarios, puedes llamarla algo como user_authentication y hacer clic en Create Key:

Create New API Key Image

Asegúrate de copiar y guardar esta clave en un lugar seguro, ya que esta es la única vez que se mostrará:

New API Key Image

Creación de la interfaz de la aplicación con FastAPI

Para crear la interfaz de la aplicación que procesa el flujo de trabajo de los emails, lo primero que debes hacer es instalar las bibliotecas necesarias. Antes de hacerlo, crea un directorio de proyecto ejecutando mkdir my_fastapi_app y pon en marcha un entorno virtual en el directorio.

Después, instala las bibliotecas necesarias con el comando siguiente:

pip install fastapi jinja2 requests uvicorn python-multipart

Aquí debes instalar lo siguiente:

  • fastapi, que sirve como el principal framework de backend
  • jinja2, que proporciona las plantillas de frontend
  • requests, que envía peticiones HTTP a la API de Mailgun

Aunque FastAPI sirve sobre todo para crear API, la incorporación de la biblioteca jinja2 permite el desarrollo full-stack, pero requiere una configuración adecuada de los directorios. La estructura de tu proyecto debe seguir este formato:

my_fastapi_app/
│── main.py # Archivo principal de la aplicación FastAPI
│── users.json # Archivo JSON donde se guardan los datos de los usuarios
│── templates/ # Carpeta de plantillas HTML
│ ├── login.html # Página de inicio de sesión
│ ├── home.html # Página de inicio
│ ├── passwordReset.html # Página para restablecer la contraseña
│── static/ # Carpeta de archivos estáticos
│ ├── styles.css # Estilos para los archivos login.html y passwordReset.html

Puedes encontrar todo el código de este tutorial en este repositorio de GitHub.

Una vez definida la estructura del proyecto, puedes copiar y pegar el código en los archivos correspondientes o bien clonar el anterior repositorio de GitHub con el siguiente comando:

git clone https://github.com/EphraimX/building-transactional-email-workflows-for-password-resets-with-mailgun-api.git

Veamos la primera sección del archivo main.py. En este archivo es donde sucede toda la magia, ya que conecta las operaciones del frontend y del backend:

                                

                                    import osrnimport jsonrnimport uuidrnimport requestsrnimport urllib.parsernfrom fastapi import FastAPI, Request, Formrnfrom pydantic import BaseModelrnfrom fastapi.responses import HTMLResponsernfrom fastapi.staticfiles import StaticFilesrnfrom fastapi.templating import Jinja2Templatesrnrnapp = FastAPI()rnapp.mount("/static", StaticFiles(directory="static"), name="static")rntemplates = Jinja2Templates(directory="templates")rnrnclass loginData(BaseModel):rn    username: strrn    password: strrnrn@app.get("/login", response_class=HTMLResponse)rnasync def login(request: Request):rn    return templates.TemplateResponse("login.html", {"request": request, "message": ""})rnrn@app.get("/passwordResetView/", response_class=HTMLResponse)rnasync def password_reset_view(request: Request):rn    return templates.TemplateResponse(rn        "passwordReset.html", {"request": request, "message": ""}rn    )rnrn@app.post("/home", response_class=HTMLResponse)rnasync def password_reset(rn    request: Request, email_address: str = Form(...), password: str = Form(...)rn):rnrn    # Open and read the JSON DB filern    try:rn        with open("users.json", "r") as file:rn            users = json.load(file)rn    except (FileNotFoundError, json.JSONDecodeError):rn        users = []rnrn    for user in users:rnrn        if user["email_address"] == email_address and user["password"] == password:rn            return templates.TemplateResponse("home.html", {"request": request})rnrn    # If no match was found, return an error messagern    return templates.TemplateResponse(rn        "login.html",rn        {rn            "request": request,rn            "message": "Email or Password Incorrect. If you cannot remember your password, kindly reset it.",rn        },rn    )rn
                                
                            

En este código, la aplicación gestiona la autenticación mostrando las páginas de inicio de sesión y de restablecer contraseñas, al tiempo que comprueba las credenciales en users.json. La aplicación inicializa FastAPI, configura las plantillas y define un modelo loginData para la validación. Las rutas /login y /passwordResetView/ ofrecen sus respectivas páginas, mientras que /home comprueba las credenciales, redirige a los usuarios válidos o muestra los errores correspondientes. La función de restablecer contraseñas genera un enlace único mediante uuid y lo envía a través de la API de Mailgun.

Para probarlo, abre tu base de datos provisional (users.json) y pega lo siguiente:

                                

                                    [rn    {rn        "username" : "sinchy",rn        "email_address" : "sinch@gmail.com",rn        "password" : "sinch12345"rn    },rn    {rn        "username" : "grinchy",rn        "email_address" : "rinch@gmail.com",rn        "password" : "grinch67890"rn    }rn]
                                
                            

También debes autorizar estas direcciones de email a través del panel de control de Mailgun si vas a usar un dominio sandbox.

Inicia la aplicación ejecutando el comando uvicorn main:app –reload. A continuación, ve a http://localhost:8000/login, donde deberías ver esto:

API Login Image

Si intentas iniciar sesión con alguna de las credenciales del archivo users.json, accederás a la página de inicio:

Successful API Login Image

Si introduces una información de inicio de sesión incorrecta, verás lo siguiente:

Unsuccessful API Login Image

Configuración de la conexión a Mailgun

Para enviar emails con Mailgun, es preciso establecer una conexión entre la aplicación y el servicio de envío de emails de Mailgun.

Para ello, abre main.py y pega el código siguiente, pero cambia , y por valores que sean válidos:

                                

                                    def send_simple_message(API_KEY):rn    response = requests.post(rn        "https://api.mailgun.net/v3/<SANDBOX_URL>/messages",rn        auth=("api", f"{API_KEY}"),rn        data={"from": "Mailgun Sandbox <postmaster@<SANDBOX_URL>>",rn              "to": "<RECIPIENT_NAME> <RECIPIENT_EMAIL>",rn              "subject": "Hello <RECIPIENT_NAME>",rn              "text": "Congratulations <RECIPIENT_NAME>, you just sent an email with Mailgun! You are truly awesome!"rn              }rn    )rnrn    print(response.json())rn    return responsern
                                
                            

Para realizar pruebas, pasa tu clave de API directamente al llamar a la función:

send_simple_message("your-api-key-here")

Pon en marcha el script ejecutando lo siguiente:

python main.py

Si todo ha ido bien, la función imprimirá una respuesta similar a esta:

{
"id": "",
"message": "Queued. Thank you."
}

Esto confirma que la solicitud de email se ha puesto en la cola correctamente mediante Mailgun.

Diseño y configuración de la plantilla de email

Para trabajar en la lógica para restablecer contraseñas, tienes que diseñar la plantilla del email que se enviará al usuario. Mailgun ofrece dos formas de diseñar un email: en HTML o con el creador de plantillas de arrastrar y soltar de Mailgun. Aquí usarás el enfoque de HTML.

Para empezar con el diseño, entra en el panel de control de Mailgun y ve a Send > Sending > Templates:

Templates Image

Después, haz clic en Create message template, selecciona la opción para programarlo en HTML y haz clic en la plantilla en blanco (Blank):

Blank Option image

Rellena el formulario de la plantilla con el nombre, la descripción y los detalles del email:

Template Details image

A continuación, desplázate hasta la pestaña Editor y copia y pega el código del archivo email_template.html :

Template Editor Image

Al seleccionar la pestaña Test Data, verás dos variables dinámicas: username y reset_link.

Selecciona la pestaña Preview para ver la plantilla:

Reset Password Image

Por último, haz clic en el botón Create para crearla.

Implementación de la funcionalidad de envío de emails

Cuando la plantilla de email esté lista, llegará el momento de crear la función de envío de emails. Abre el archivo main.py y pega el siguiente código:

                                

                                    @app.post("/resetPassword/")rnasync def reset_password(request: Request, email_address: str = Form(...)):rnrn    # Open and read the JSON DB filern    try:rn        with open("users.json", "r") as file:rn            users = json.load(file)rn    except (FileNotFoundError, json.JSONDecodeError):rn        users = []rnrn    for user in users:rn        if user["email_address"] == email_address:rnrn            email_address = user["email_address"]rn            username = user["username"]rn            reset_link = await generate_password_reset_link(email_address)rnrn            response = await send_password_reset_email(rn                username, email_address, reset_linkrn            )rnrn            if response["success"]:rnrn                return templates.TemplateResponse(rn                    "login.html",rn                    {rn                        "request": request,rn                        "message": "Kindly check your email to reset your password.",rn                    },rn                )rnrn            else:rnrn                return templates.TemplateResponse(rn                    "passwordReset.html",rn                    {rn                        "request": request,rn                        "message": "Unfortunately, we ran into an error while trying to reset your password. Kindly try again, and if the issue persists, please contact support.",rn                    },rn                )rnrn    # If no match was found, return an error messagern    return templates.TemplateResponse(rn        "passwordReset.html",rn        {rn            "request": request,rn            "message": "Unfortunately, we do not have a record of your email. Kindly reach out to the site administrator.",rn        },rn    )rnrnasync def send_password_reset_email(username, email_address, reset_link):rnrn    API_KEY = os.getenv("API_KEY")  # Fetch API key from environmentrnrn    if not API_KEY:rn        print("Error: API_KEY is not set!")rn        return {"success": False, "error": "API key is missing"}rnrn    try:rn        response = requests.post(rn            "https://api.mailgun.net/v3/<SANDBOX_URL>/messages",rn            auth=("api", API_KEY),rn            data={rn                "from": "Mailgun Sandbox <postmaster@<SANDBOX_URL>>",rn                "to": f"{username} <{email_address}>",rn                "subject": f"Hello {username}",rn                "template": "<EMAIL_TEMPLATE_NAME>",rn                "h:X-Mailgun-Variables": json.dumps(rn                    {"reset_link": reset_link, "username": username}rn                ),rn            },rn        )rnrn        if response.status_code == 200:rn            print(f"Password reset email sent successfully to {email_address}")rn            return {"success": True, "message": "Email sent successfully"}rn        else:rn            print(rn                f"Failed to send email. Status Code: {response.status_code}, Response: {response.text}"rn            )rn            return {"success": False, "error": response.text}rnrn    except requests.exceptions.RequestException as e:rn        print(f"Request failed: {str(e)}")rn        return {"success": False, "error": str(e)}rnrnasync def generate_password_reset_link(rn    email_address, base_url="https://mailgunny.com/reset-password"rn):rn    token = uuid.uuid4()rn    encoded_email = urllib.parse.quote(email_address)rn    return f"{base_url}?email={encoded_email}&token={token}"rn
                                
                            

En este código, la función reset_password (@app.post("/resetPassword/")) procesa las solicitudes para restablecer contraseñas comprobando si el email enviado figura en la base de datos de users.json. Si es así, genera un enlace de restablecimiento único mediante generate_password_reset_link(email_address) y envía el email para restablecer la contraseña a través de send_password_reset_email(username, email_address, reset_link). A continuación, la función devuelve un mensaje de éxito si el email se ha enviado o bien muestra un mensaje de error si no es así. Si no se encuentra ningún email coincidente, se devuelve la respuesta de error correspondiente.

Nota: Sustituye la variable API_KEY por la clave de tu cuenta de Mailgun.

Para probar la implementación, ejecuta uvicorn main:app –reload y abre http://localhost:8000/login. Haz clic en el enlace Forgot Password? :

Forgot Password Link Image

Introduce un email que se encuentre en tu archivo users.json y haz clic en Reset Password:

Forgot Password Reset Image

Si va bien, volverás a la página de inicio de sesión, donde verás las instrucciones para revisar tu email. Deberías ver el siguiente email en la bandeja de entrada del destinatario:

Reset Password Request Image

Integración con eventos de aplicaciones

La integración con los eventos de la aplicación mediante webhooks permite que tu sistema responda de forma automática a las actualizaciones en tiempo real de servicios externos.

En esencia, un webhook es una petición HTTP de devolución de llamada. Siempre que se produce un evento específico en una aplicación externa (como la confirmación de un pago, el registro de un usuario o una solicitud para restablecer contraseñas), el servicio envía una petición a tu URL de webhook predefinida. De este modo se logra una automatización sin fisuras que reduce la necesidad de intervención manual o de sondeos periódicos. Al configurar tu aplicación para que intercepte estos eventos de webhook, puedes activar acciones pertinentes como actualizar una base de datos, enviar notificaciones o procesar transacciones en el instante en que se produzca el evento.

Para integrar los webhooks con Mailgun, tienes que crear un punto de conexión. Para ello, abre el archivo main.py y pega el código siguiente al final:

                                

                                    @app.post("/webhooks/password-reset")rnasync def handle_webhook(request: Request):rn    data = await request.json()rn    print(f"Password reset email clicked: {data}")rn    return {"status": "received", "status_code": 200}rn
                                
                            

Vuelve al panel de control y dirígete a Send > Sending > Webhooks:

Add Webhooks Image

Haz clic en Add webhook en la esquina superior derecha. En la lista desplegable de tipos de eventos, selecciona Delivered Messages, y en el campo URL, introduce la ruta a tu URL del webhook. En este caso, se trata de https:///webhooks/password-reset. Al terminar, haz clic en Create webhook:

New Webhook Image

Prueba el webhook. El resultado debería tener este aspecto:

Test webhook Image

En el lado del servidor, tendrías que recibir el siguiente mensaje:

Password reset email clicked: {'signature': {'token': '41929b08bb287b8a0b157f7834844b6bc483b7b090692a2b15', 'timestamp': '1739785355', 'signature': '117f70dcf985c72df30c07b02898dd458d1cbbad29d0457ccee8c4497f89c5ac'}, 'event-data': {'id': 'CPgfbmQMTCKtHW6uIWtuVe', 'timestamp': 1521472262.908181, 'log-level': 'info', 'event': 'delivered', 'delivery-status': {'tls': True, 'mx-host': 'smtp-in.example.com', 'code': 250, 'description': '', 'session-seconds': 0.4331989288330078, 'utf8': True, 'attempt-no': 1, 'message': 'OK', 'certificate-verified': True}, 'flags': {'is-routed': False, 'is-authenticated': True, 'is-system-test': False, 'is-test-mode': False}, 'envelope': {'transport': 'smtp', 'sender': 'bob@sandbox9199067bcb654265aae9ce9e308b6150.mailgun.org', 'sending-ip': '209.61.154.250', 'targets': 'alice@example.com'}, 'message': {'headers': {'to': 'Alice ', 'message-id': '20130503182626.18666.16540@sandbox9199067bcb654265aae9ce9e308b6150.mailgun.org', 'from': 'Bob ', 'subject': 'Test delivered webhook'}, 'attachments': [], 'size': 111}, 'recipient': 'alice@example.com', 'recipient-domain': 'example.com', 'storage': {'url': 'https://se.api.mailgun.net/v3/domains/sandbox9199067bcb654265aae9ce9e308b6150.mailgun.org/messages/message_key', 'key': 'message_key'}, 'campaigns': [], 'tags': ['my_tag_1', 'my_tag_2'], 'user-variables': {'my_var_1': 'Mailgun Variable #1', 'my-var-2': 'awesome'}}}

Esto puede variar en función del email que se envíe.

Seguimiento y gestión de emails

Peter Drucker, un famoso consultor de gestión, dijo una vez:

Lo que no se puede medir, no se puede mejorar.

En este sentido, Mailgun ofrece herramientas para comprobar el rendimiento de los emails enviados. Por ejemplo, la página Send > Sending > Analytics ayuda a medir qué porcentaje de emails se están entregando, abriendo y recibiendo clics:

Analytics Image

En la página Send > Reporting > Metrics, puedes consultar información detallada sobre el rendimiento de tus emails:

Metrics image

También puedes elegir las métricas que te interesa ver a lo largo de un período determinado:

Customized Metrics Image

Mailgun también te proporciona registros por si quieres investigar a fondo los eventos entregados y analizar los errores cuando se produzcan:

Logs Image

Por último, Mailgun ofrece una clasificación de los rebotes para que puedas comprobar cuántos de tus emails no llegan a su destino. También te permite saber cuántos de esos rebotes son críticos:

Bounce Classification Image

En resumen

En este tutorial, has aprendido a crear un flujo de trabajo completo de emails transaccionales para restablecer contraseñas con la API de Mailgun. Tras configurar una cuenta de Mailgun y obtener una clave de API, has creado la interfaz de una aplicación mediante FastAPI. Después, has diseñado y configurado una plantilla de email, has implementado la función de enviar emails y has integrado webhooks para hacer un seguimiento de los eventos en tiempo real.

Las operaciones de los emails transaccionales son de suma importancia a la hora de proporcionar una experiencia de usuario fluida y segura, sobre todo para restablecer contraseñas. Mailgun puede ayudarte a automatizar este proceso, mejorando la seguridad y la productividad, y acabando con la intervención manual. La adopción de este enfoque en tus proyectos no solo incrementará la satisfacción de la clientela, sino que también contribuirá a conservar un sistema de autenticación fiable.