{"id":19770,"date":"2025-06-11T00:00:00","date_gmt":"2025-06-11T00:00:00","guid":{"rendered":"https:\/\/www.mailgun.com\/blog\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\/"},"modified":"2026-09-14T15:17:59","modified_gmt":"2026-09-14T15:17:59","slug":"como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio","status":"publish","type":"blog","link":"https:\/\/www.mailgun.com\/es\/blog\/dev-life\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\/","title":{"rendered":"Creaci\u00f3n de flujos de trabajo de emails transaccionales para notificaciones de env\u00edo con la API de Mailgun"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Cuando pides algo por internet, esperas saber exactamente d\u00f3nde est\u00e1 y cu\u00e1ndo llegar\u00e1. Los flujos de trabajo de emails transaccionales ayudan a hacer posible este tipo de comunicaci\u00f3n puntual.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A diferencia de los emails de marketing que promocionan productos, los emails transaccionales se env\u00edan en respuesta a la acci\u00f3n de un usuario, como realizar un pedido u obtener una actualizaci\u00f3n de env\u00edo. Proporcionan detalles en tiempo real que mantienen a los usuarios al tanto en cada etapa del proceso.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En este art\u00edculo, aprender\u00e1s a crear un flujo de trabajo de emails transaccionales para notificaciones de env\u00edo con  <a href=\"https:\/\/documentation.mailgun.com\/\" target=\"_tabs\" rel=\"noopener noreferrer\">la API de Mailgun<\/a>. El flujo de trabajo incorporar\u00e1  <a href=\"https:\/\/docs.bullmq.io\/\" target=\"_tabs\" rel=\"noopener noreferrer\">BullMQ<\/a>, un potente sistema de colas de trabajos, para poner en cola y procesar notificaciones por email de forma separada a la l\u00f3gica principal de la aplicaci\u00f3n y mejorar as\u00ed la escalabilidad y el rendimiento. Esto resulta \u00fatil para aplicaciones con mucho tr\u00e1fico en las que enviar emails directamente dentro de un ciclo de solicitud y respuesta podr\u00eda ralentizar el sistema.<\/p>\n\n\n\r\n    <nav data-content-type=\"longform\" class=\"toc-block longform-spacings px-5 py-6 px-md-6 px-lg-7 py-md-7 bg-light fs-sm rounded-lg\" aria-labelledby=\"toc-title-7363\"><p class=\"h5 m-0\" id=\"toc-title-7363\">\u00cdndice<\/p><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">01<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#implementacion-de-flujos-de-trabajo-de-emails-transaccionales-para-notificaciones-de-envio-con-la-api-de-mailgun\">Implementaci\u00f3n de flujos de trabajo de emails transaccionales para notificaciones de env\u00edo con la API de Mailgun<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">02<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#configuracion-de-la-cuenta-de-mailgun\">Configuraci\u00f3n de la cuenta de Mailgun<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">03<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#configuracion-de-la-plantilla-de-inicio\">Configuraci\u00f3n de la plantilla de inicio<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">04<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#instala-todas-las-dependencias-usando-el-siguiente-comando\">Instala todas las dependencias usando el siguiente comando:<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">05<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#diseno-de-las-plantillas-de-email\">Dise\u00f1o de las plantillas de email<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">06<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#implementacion-de-la-funcionalidad-de-envio-de-emails\">Implementaci\u00f3n de la funcionalidad de env\u00edo de emails<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">07<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#integracion-con-eventos-de-aplicaciones\">Integraci\u00f3n con eventos de aplicaciones<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">08<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#probar-el-flujo-de-trabajo\">Probar el flujo de trabajo<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">09<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#seguimiento-y-gestion-de-emails\">Seguimiento y gesti\u00f3n de emails<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">10<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#en-resumen\">En resumen<\/a><\/div><\/div><\/nav>\n\n\n<h2 class=\"wp-block-heading\">Implementaci\u00f3n de flujos de trabajo de emails transaccionales para notificaciones de env\u00edo con la API de Mailgun<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Para seguir este tutorial, necesitas estas herramientas y cuentas:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Un <a href=\"https:\/\/signup.mailgun.com\/new\/signup\" target=\"_tabs\" rel=\"noopener noreferrer\">Cuenta de Mailgun<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/nodejs.org\/en\/download\" target=\"_tabs\" rel=\"noopener noreferrer\">Node.js<\/a> y <a href=\"https:\/\/docs.docker.com\/engine\/install\/\" target=\"_tabs\" rel=\"noopener noreferrer\">Docker<\/a>  instalado en tu equipo local<\/li>\n\n\n\n<li>Un editor de c\u00f3digo (este tutorial utiliza  <a href=\"https:\/\/code.visualstudio.com\/\" target=\"_tabs\" rel=\"noopener noreferrer\">VS Code<\/a>) y un navegador web<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Antes de profundizar en la implementaci\u00f3n, echemos un vistazo al flujo de trabajo:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" class=\"wp-image-3943\" width=\"2252\" height=\"1078\" src=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/1__3_.png\" alt=\"transactional email workflows diagram\" srcset=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/1__3_.png 2252w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/1__3_-300x144.png 300w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/1__3_-768x368.png 768w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/1__3_-1024x490.png 1024w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/1__3_-1536x735.png 1536w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/1__3_-2048x980.png 2048w\" sizes=\"(max-width: 2252px) 100vw, 2252px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">A continuaci\u00f3n te explicamos c\u00f3mo funciona el proceso:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>El equipo de env\u00edos (o cualquier persona encargada de gestionar las actualizaciones de env\u00edo) env\u00eda una solicitud HTTP para actualizar el estado del pedido<\/li>\n\n\n\n<li>El servidor Express actualiza el estado del pedido en la base de datos SQLite y pone en cola un trabajo en BullMQ<\/li>\n\n\n\n<li>Un proceso de trabajo de BullMQ procesa los trabajos de la cola e interact\u00faa con la API de Mailgun para enviar un email al cliente<\/li>\n\n\n\n<li>El proceso de trabajo reintenta autom\u00e1ticamente los trabajos fallidos<\/li>\n\n\n\n<li>Mailgun entrega el email al cliente<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Configuraci\u00f3n de la cuenta de Mailgun<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Para usar la API de Mailgun, necesitas un dominio de Mailgun y una clave de API para la autenticaci\u00f3n. Mailgun proporciona dos tipos de dominios:  <a href=\"https:\/\/documentation.mailgun.com\/docs\/mailgun\/user-manual\/domains\/#sandbox-domain\" target=\"_tabs\" rel=\"noopener noreferrer\">dominios sandbox<\/a>  para pruebas y  <a href=\"https:\/\/documentation.mailgun.com\/docs\/mailgun\/user-manual\/domains\/#custom-domains\" target=\"_tabs\" rel=\"noopener noreferrer\">dominios personalizados<\/a>  para su uso en entornos de producci\u00f3n.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En esta gu\u00eda, utilizar\u00e1s un dominio sandbox. Cada cuenta de Mailgun incluye uno de forma autom\u00e1tica, por lo que no es necesario que lo crees manualmente. Para obtener tu dominio sandbox, inicia sesi\u00f3n en tu cuenta de Mailgun y, a continuaci\u00f3n, ve a <b>Send &gt; Sending &gt; Domains<\/b> en la barra lateral. Copia tu dominio:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" class=\"wp-image-3944\" width=\"2010\" height=\"1018\" src=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/2__3_.png\" alt=\"Send &gt; Sending &gt; Domains Screen Image\" srcset=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/2__3_.png 2010w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/2__3_-300x152.png 300w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/2__3_-768x389.png 768w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/2__3_-1024x519.png 1024w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/2__3_-1536x778.png 1536w\" sizes=\"(max-width: 2010px) 100vw, 2010px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Por defecto, un dominio sandbox solo puede enviar emails a  <a href=\"https:\/\/help.mailgun.com\/hc\/en-us\/articles\/217531258-Authorized-Recipients\" target=\"_tabs\" rel=\"noopener noreferrer\">destinatarios autorizados<\/a>. . Para configurar un email como destinatario autorizado, haz clic en tu nombre de dominio en la p\u00e1gina <b>Domains<\/b> para ver sus ajustes. En la pesta\u00f1a <b>Setup<\/b>, en <b>Add authorized recipients<\/b>, a\u00f1ade la direcci\u00f3n de email a la que quieres enviar emails. Aseg\u00farate de seguir las instrucciones enviadas a la direcci\u00f3n de email que has proporcionado para configurarla como destinatario autorizado:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" class=\"wp-image-3947\" width=\"2006\" height=\"1012\" src=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/3__3_.png\" alt=\"Add authorized recipients Screen Image\" srcset=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/3__3_.png 2006w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/3__3_-300x151.png 300w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/3__3_-768x387.png 768w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/3__3_-1024x517.png 1024w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/3__3_-1536x775.png 1536w\" sizes=\"(max-width: 2006px) 100vw, 2006px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Para obtener tu clave de API, ve a la <a href=\"https:\/\/app.mailgun.com\/settings\/api_security\" target=\"_tabs\" rel=\"noopener noreferrer\">p\u00e1gina <b>API Keys<\/b><\/a> y haz clic en <b>Add new key<\/b> para crear una nueva clave de API. Dale un nombre a la clave y haz clic en <b>Create Key<\/b>. A continuaci\u00f3n, aseg\u00farate de copiar el valor de tu clave porque solo se mostrar\u00e1 una vez:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" class=\"wp-image-3948\" width=\"2012\" height=\"1022\" src=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/4__4_.png\" alt=\"API Keys page Screen Image\" srcset=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/4__4_.png 2012w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/4__4_-300x152.png 300w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/4__4_-768x390.png 768w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/4__4_-1024x520.png 1024w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/4__4_-1536x780.png 1536w\" sizes=\"(max-width: 2012px) 100vw, 2012px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Configuraci\u00f3n de la plantilla de inicio<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Para que este tutorial se centre en la creaci\u00f3n de flujos de trabajo de emails transaccionales, hemos preparado una plantilla de inicio para que puedas desarrollarla. Clona la plantilla en tu m\u00e1quina local ejecutando el siguiente comando en tu terminal:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>git clone --single-branch -b starter-template https:\/\/github.com\/kimanikevin254\/mailgun-transactional-email-workflows.git<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Estos son los archivos clave del proyecto:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>src\/controllers\/order.controller.ts contiene<\/code> dos m\u00e9todos:\n<ul class=\"wp-block-list\">\n<li><code>index<\/code> renderiza la interfaz de administraci\u00f3n, que se define en <code>src\/views\/admin.ejs<\/code><\/li>\n\n\n\n<li><code>updateOrderStatus<\/code> actualiza el estado del pedido en la base de datos <\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><code>src\/database\/entity\/order.entity.ts<\/code> define la entidad de pedido<\/li>\n\n\n\n<li><code>src\/database\/entity\/user.entity.ts<\/code> define la entidad de usuario<\/li>\n\n\n\n<li><code>src\/database\/seed.ts<\/code> llena la base de datos con un usuario y un pedido de muestra<\/li>\n\n\n\n<li><code>docker-compose.yml ejecuta<\/code> <a href=\"https:\/\/redis.io\/\" target=\"_tabs\" rel=\"noopener noreferrer\">Redis<\/a> en Docker, que es necesario para BullMQ<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Instala todas las dependencias usando el siguiente comando:<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>npm install<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A continuaci\u00f3n, cambia el nombre del <code>archivo<\/code> <code>.env.example<\/code> a <code>.env <\/code>y sustituye los marcadores de posici\u00f3n de <code>MAILGUN_DOMAIN <\/code>y <code>MAILGUN_API_KEY<\/code> por las credenciales que obtuviste en el panel de control de Mailgun. Para la variable MAIL_FROM, proporciona tu direcci\u00f3n de email como valor.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A continuaci\u00f3n, abre el <code>archivo<\/code> <code>src\/database\/seed.ts<\/code> y sustituye los marcadores de posici\u00f3n de la <code>constante userInfo<\/code> por tu informaci\u00f3n. Para el email, aseg\u00farate de proporcionar uno que hayas a\u00f1adido como destinatario autorizado.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Finalmente, llena la base de datos ejecutando el comando <code>npm run db:seed<\/code> en tu terminal. Ahora puedes ejecutar el proyecto con el comando <code>npm run dev<\/code> y visitar <a href=\"http:\/\/localhost:3000\/\" target=\"_tabs\" rel=\"noopener noreferrer\">http:\/\/localhost:3000<\/a> en tu navegador para ver y actualizar el estado del pedido:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" class=\"wp-image-3953\" width=\"2028\" height=\"536\" src=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/5__4_.png\" alt=\"order status management screen image\" srcset=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/5__4_.png 2028w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/5__4_-300x79.png 300w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/5__4_-768x203.png 768w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/5__4_-1024x271.png 1024w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/5__4_-1536x406.png 1536w\" sizes=\"(max-width: 2028px) 100vw, 2028px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">En la siguiente secci\u00f3n, implementar\u00e1s un flujo de trabajo que env\u00eda autom\u00e1ticamente un email al usuario cuando se actualiza el estado del pedido.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Dise\u00f1o de las plantillas de email<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Las plantillas de email bien dise\u00f1adas mejoran la legibilidad, enriquecen la experiencia de usuario y garantizan que tus emails se vean profesionales en distintos dispositivos. En esta secci\u00f3n, crear\u00e1s plantillas que har\u00e1n que tus emails transaccionales sean claros, atractivos y eficaces.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Un pedido pasa por tres estados (enviado, en reparto y entregado), tal y como se define en el <code>tipo OrderStatus<\/code> de <code>src\/types\/index.ts.<\/code> Crear\u00e1s una plantilla de email correspondiente para cada estado para mantener a los clientes informados en cada paso.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para ello, crea un archivo en <code>src\/email-templates\/html\/shipped.html<\/code> y a\u00f1ade el siguiente c\u00f3digo:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                HTML                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-markup\">\r\n                                <code id=\"code_0_content\" class=\"language-markup\">\r\n\r\n                                    &lt;!DOCTYPE html&gt;rn&lt;html&gt;rn&lt;head&gt;rn    &lt;meta charset=&quot;UTF-8&quot; \/&gt;rn    &lt;title&gt;Order Shipped&lt;\/title&gt;rn&lt;\/head&gt;rn&lt;body style=&quot;font-family: Arial, sans-serif; background-color: #f4f4f4; padding: 20px; text-align: center;&quot;&gt;rnrn    &lt;div style=&quot;background: #ffffff; padding: 20px; border-radius: 8px; max-width: 600px; margin: auto; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);&quot;&gt;rnrn        &lt;div style=&quot;background-color: #007bff; color: white; padding: 15px; font-size: 22px; font-weight: bold; border-top-left-radius: 8px; border-top-right-radius: 8px;&quot;&gt;rn            Your Order Has Been Shipped! \u2708\ufe0frn        &lt;\/div&gt;rnrn        &lt;div style=&quot;padding: 20px; font-size: 16px; color: #333; text-align: left;&quot;&gt;rn            &lt;p style=&quot;margin: 0 0 10px;&quot;&gt;Hello, &lt;%= name %&gt;&lt;\/p&gt;rn            &lt;p style=&quot;margin: 0 0 10px;&quot;&gt;Your order has been shipped and is on its way to you. \ud83d\udce6&lt;\/p&gt;rn            &lt;p style=&quot;margin: 0 0 10px;&quot;&gt;&lt;strong&gt;Tracking Number:&lt;\/strong&gt; &lt;%= trackingNumber %&gt;&lt;\/p&gt;rn            &lt;p style=&quot;margin: 0 0 10px;&quot;&gt;You can track your package below. We hope you enjoy your purchase!&lt;\/p&gt;rnrn            &lt;a href=&quot;https:\/\/yourwebsite.com\/track?trackingNumber=&lt;%= trackingNumber %&gt;&quot;rn               style=&quot;background-color: #007bff; color: white; text-decoration: none; padding: 12px 18px; border-radius: 5px; display: inline-block; margin-top: 15px; font-weight: bold;&quot;&gt;rn                Track Your Shipmentrn            &lt;\/a&gt;rn        &lt;\/div&gt;rnrn        &lt;div style=&quot;background-color: #f4f4f4; padding: 10px; font-size: 14px; color: #555; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;&quot;&gt;rn            Thank you for shopping with us! \ud83d\uded2rn        &lt;\/div&gt;rn    &lt;\/div&gt;rn&lt;\/body&gt;rn&lt;\/html&gt;rn\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Esta plantilla de email se utiliza para notificar a los clientes que su pedido ha sido enviado. Incluye marcadores de posici\u00f3n para el nombre del cliente y el n\u00famero de seguimiento, que se rellenar\u00e1n din\u00e1micamente mediante <a href=\"https:\/\/ejs.co\/\" target=\"_tabs\" rel=\"noopener noreferrer\">EJS<\/a> al renderizar la plantilla. Las plantillas para los otros dos estados del pedido seguir\u00e1n el mismo formato.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Crea una plantilla para notificar a los usuarios que su pedido est\u00e1 en reparto. Para ello, crea un archivo llamado <code>out_for_delivery.html<\/code> en la carpeta <code>src\/email-templates\/html<\/code> y a\u00f1ade el siguiente c\u00f3digo:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                HTML                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-markup\">\r\n                                <code id=\"code_0_content\" class=\"language-markup\">\r\n\r\n                                    &lt;!DOCTYPE html&gt;rn&lt;html&gt;rn&lt;head&gt;rn    &lt;meta charset=&quot;UTF-8&quot; \/&gt;rn    &lt;title&gt;Out for Delivery&lt;\/title&gt;rn&lt;\/head&gt;rn&lt;body style=&quot;font-family: Arial, sans-serif; background-color: #f4f4f4; padding: 20px; text-align: center;&quot;&gt;rnrn    &lt;div style=&quot;background: #ffffff; padding: 20px; border-radius: 8px; max-width: 600px; margin: auto; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);&quot;&gt;rnrn        &lt;div style=&quot;background-color: #28a745; color: white; padding: 15px; font-size: 22px; font-weight: bold; border-top-left-radius: 8px; border-top-right-radius: 8px;&quot;&gt;rn            Your Order Is Out for Delivery! \ud83d\ude9arn        &lt;\/div&gt;rnrn        &lt;div style=&quot;padding: 20px; font-size: 16px; color: #333; text-align: left;&quot;&gt;rn            &lt;p style=&quot;margin: 0 0 10px;&quot;&gt;Hello, &lt;%= name %&gt;&lt;\/p&gt;rn            &lt;p style=&quot;margin: 0 0 10px;&quot;&gt;Good news! Your package is out for delivery and will arrive soon. \ud83d\udce6&lt;\/p&gt;rn            &lt;p style=&quot;margin: 0 0 10px;&quot;&gt;&lt;strong&gt;Tracking Number:&lt;\/strong&gt; &lt;%= trackingNumber %&gt;&lt;\/p&gt;rn            &lt;p style=&quot;margin: 0 0 10px;&quot;&gt;We appreciate your business and can&#039;t wait for you to receive your order!&lt;\/p&gt;rnrn            &lt;a href=&quot;https:\/\/yourwebsite.com\/track?trackingNumber=&lt;%= trackingNumber %&gt;&quot;rn               style=&quot;background-color: #28a745; color: white; text-decoration: none; padding: 12px 18px; border-radius: 5px; display: inline-block; margin-top: 15px; font-weight: bold;&quot;&gt;rn                Track Your Packagern            &lt;\/a&gt;rn        &lt;\/div&gt;rnrn        &lt;div style=&quot;background-color: #f4f4f4; padding: 10px; font-size: 14px; color: #555; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;&quot;&gt;rn            Thank you for choosing us! \ud83d\udecd\ufe0frn        &lt;\/div&gt;rn    &lt;\/div&gt;rn&lt;\/body&gt;rn&lt;\/html&gt;\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Por \u00faltimo, crea una plantilla para notificar a los usuarios que su pedido ha sido entregado. Para ello, crea un archivo llamado <code>delivered.html<\/code> en la <code>carpeta<\/code> <code>src\/email-templates\/html<\/code> y a\u00f1ade el siguiente c\u00f3digo:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                HTML                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-markup\">\r\n                                <code id=\"code_0_content\" class=\"language-markup\">\r\n\r\n                                    &lt;!DOCTYPE html&gt;rn&lt;html&gt;rn&lt;head&gt;rn    &lt;meta charset=&quot;UTF-8&quot; \/&gt;rn    &lt;title&gt;Order Delivered&lt;\/title&gt;rn&lt;\/head&gt;rn&lt;body style=&quot;font-family: Arial, sans-serif; background-color: #f4f4f4; padding: 20px; text-align: center;&quot;&gt;rnrn    &lt;div style=&quot;background: #ffffff; padding: 20px; border-radius: 8px; max-width: 600px; margin: auto; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);&quot;&gt;rnrn        &lt;div style=&quot;background-color: #ffc107; color: white; padding: 15px; font-size: 22px; font-weight: bold; border-top-left-radius: 8px; border-top-right-radius: 8px;&quot;&gt;rn            Your Order Has Been Delivered! \ud83c\udf89rn        &lt;\/div&gt;rnrn        &lt;div style=&quot;padding: 20px; font-size: 16px; color: #333; text-align: left;&quot;&gt;rn            &lt;p style=&quot;margin: 0 0 10px;&quot;&gt;Hello, &lt;%= name %&gt;&lt;\/p&gt;rn            &lt;p style=&quot;margin: 0 0 10px;&quot;&gt;We&#039;re happy to inform you that your package has been successfully delivered. \ud83c\udf81&lt;\/p&gt;rn            &lt;p style=&quot;margin: 0 0 10px;&quot;&gt;&lt;strong&gt;Tracking Number:&lt;\/strong&gt; &lt;%= trackingNumber %&gt;&lt;\/p&gt;rn            &lt;p style=&quot;margin: 0 0 10px;&quot;&gt;We hope you enjoy your purchase! If you have any questions, feel free to contact us.&lt;\/p&gt;rnrn            &lt;a href=&quot;https:\/\/yourwebsite.com\/track?trackingNumber=&lt;%= trackingNumber %&gt;&quot;rn               style=&quot;background-color: #ffc107; color: white; text-decoration: none; padding: 12px 18px; border-radius: 5px; display: inline-block; margin-top: 15px; font-weight: bold;&quot;&gt;rn                Track Your Orderrn            &lt;\/a&gt;rn        &lt;\/div&gt;rnrn        &lt;div style=&quot;background-color: #f4f4f4; padding: 10px; font-size: 14px; color: #555; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;&quot;&gt;rn            Thank you for shopping with us! \ud83d\udecd\ufe0frn        &lt;\/div&gt;rn    &lt;\/div&gt;rn&lt;\/body&gt;rn&lt;\/html&gt;\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Para gestionar y renderizar plantillas de email de forma din\u00e1mica, tienes que crear una clase que cargue y organice las plantillas para los diferentes estados de los pedidos. Esta clase recuperar\u00e1 la plantilla correcta y la renderizar\u00e1 con los datos espec\u00edficos de cada cliente. Para ello, crea un nuevo archivo llamado <code>index.ts<\/code> en la <code>carpeta src\/email-templates <\/code>y a\u00f1ade el siguiente c\u00f3digo:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    import { readFileSync } from &quot;fs&quot;;rnimport { EmailTemplate, OrderStatus } from &quot;..\/types&quot;;rnimport ejs from &quot;ejs&quot;;rnrnexport class EmailTemplateManager {rn    private templates: Map&lt;OrderStatus, EmailTemplate&gt;;rnrn    constructor() {rn        this.templates = new Map();rn        this.loadTemplates();rn    }rnrn    private loadTemplates() {rn        \/\/ Load each template for different statusesrn        const statuses: OrderStatus[] = [rn            &quot;shipped&quot;,rn            &quot;out_for_delivery&quot;,rn            &quot;delivered&quot;,rn        ];rn        const subjects: Record&lt;OrderStatus, string&gt; = {rn            shipped: &quot;Your Order Has Been Shipped! \u2708\ufe0f&quot;,rn            out_for_delivery: &quot;Your Order is Out for Delivery! \ud83d\ude9a&quot;,rn            delivered: &quot;Your Order Has Been Delivered! \ud83c\udf89&quot;,rn        };rnrn        statuses.forEach((status) =&gt; {rn            const htmlTemplate = readFileSync(rn                `src\/email-templates\/html\/${status}.html`,rn                &quot;utf-8&quot;rn            );rnrn            this.templates.set(status, {rn                subject: subjects[status],rn                html: htmlTemplate,rn            });rn        });rn    }rnrn    getTemplate(status: OrderStatus): EmailTemplate {rn        const template = this.templates.get(status);rn        if (!template) {rn            throw new Error(`No template found for status: ${status}`);rn        }rn        return template;rn    }rnrn    renderTemplate(templateString: string, data: Record&lt;string, any&gt;): string {rn        return ejs.render(templateString, data); \/\/ Render the template with the provided datarn    }rn}\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Esta clase inicializa una variable privada llamada templates que almacena plantillas de email en un objeto Map para poder buscarlas r\u00e1pidamente en funci\u00f3n del estado del pedido. Usar un Map garantiza una recuperaci\u00f3n eficiente y evita tener que leer repetidamente los archivos del disco. La clase tambi\u00e9n define los siguientes m\u00e9todos:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>loadTemplates<\/code> (privado) lee las plantillas HTML de los archivos y las almacena en el Map templates, junto con sus correspondientes l\u00edneas de asunto.<\/li>\n\n\n\n<li><code>getTemplate<\/code> recupera la plantilla de email para un determinado estado del pedido y arroja un error si no se encuentra ninguna plantilla.<\/li>\n\n\n\n<li><code>renderTemplate<\/code> utiliza EJS para renderizar din\u00e1micamente una plantilla con los datos proporcionados.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Implementaci\u00f3n de la funcionalidad de env\u00edo de emails<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Con las plantillas de email listas, el siguiente paso es implementar la funcionalidad para enviar notificaciones. Esto implica poner en cola los trabajos de email y procesarlos de forma as\u00edncrona.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pero antes de poder hacerlo, tienes que instalar algunas dependencias ejecutando el siguiente comando:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>npm i mailgun.js@11.1.0 form-data@4.0.2 bullmq@5.41.2 ioredis@5.5.0<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aqu\u00ed tienes un resumen de cada una de estas dependencias:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>mailgun.js<\/code> es un cliente de Node.js para interactuar con la API de Mailgun. Lo usar\u00e1s para enviar emails transaccionales.<\/li>\n\n\n\n<li><code>form-data<\/code> es un paquete que requiere <code>mailgun.js<\/code> para manejar los env\u00edos de formularios al mandar emails.<\/li>\n\n\n\n<li><code>bullmq<\/code> es una biblioteca de colas de trabajos para manejar tareas as\u00edncronas. Te permite poner en cola las notificaciones por email y procesarlas en segundo plano.<\/li>\n\n\n\n<li><code>ioredis<\/code> es un cliente de Redis para Node.js que utiliza BullMQ para almacenar y gestionar los trabajos en cola.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">A continuaci\u00f3n, tienes que definir un cliente de la API de Mailgun que usar\u00e1s para enviar emails. Para ello, crea un nuevo archivo en <code>src\/config\/mailgun.config.ts<\/code> y a\u00f1ade el siguiente c\u00f3digo:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    import FormData from &quot;form-data&quot;;rnimport Mailgun from &quot;mailgun.js&quot;;rnrnconst mailgun = new Mailgun(FormData);rnexport const mgClient = mailgun.client({rn    username: &quot;api&quot;,rn    key: process.env.MAILGUN_API_KEY,rn});\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Este c\u00f3digo configura y exporta un cliente de Mailgun usando la biblioteca <code>mailgun.js<\/code> y <code>form-data<\/code>. Esto permite a tu aplicaci\u00f3n enviar emails mediante la API de Mailgun con la clave de API almacenada en variables de entorno.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ahora, tienes que definir una conexi\u00f3n de Redis que BullMQ utilizar\u00e1 para almacenar y gestionar trabajos en segundo plano. Para ello, crea un nuevo archivo llamado <code>redis.config.ts<\/code> en la carpeta <code>src\/config<\/code> y a\u00f1ade el siguiente c\u00f3digo:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    import IORedis from &quot;ioredis&quot;;rnrnexport const redisConfig = new IORedis({rn    host: process.env.REDIS_HOST,rn    port: parseInt(process.env.REDIS_PORT),rn    maxRetriesPerRequest: null,rn});\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Tienes que configurar una cola de BullMQ para las notificaciones de env\u00edo que gestionar\u00e1 los trabajos de notificaci\u00f3n por email en segundo plano mediante la conexi\u00f3n de Redis que definiste en el paso anterior. Para ello, crea un nuevo archivo llamado <code>queues.config.ts<\/code> en la carpeta <code>src\/config<\/code> y a\u00f1ade el siguiente c\u00f3digo:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    import { Queue } from &quot;bullmq&quot;;rnimport { redisConfig } from &quot;.\/redis.config&quot;;rnrnexport const QUEUE_NAMES = {rn    SHIPPING_NOTICATIONS: &quot;shipping-notifications&quot;,rn};rnrnexport const JOB_NAMES = {rn    SEND_NOTIFICATION: &quot;send-notification&quot;,rn};rnrnexport const SHIPPING_NOTICATIONS_QUEUE = new Queue(rn    QUEUE_NAMES.SHIPPING_NOTICATIONS,rn    {rn        connection: redisConfig,rn    }rn);\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Este c\u00f3digo tambi\u00e9n define nombres para la cola y el tipo de trabajo que pueden utilizarse en otras partes de la aplicaci\u00f3n para evitar usar cadenas incrustadas en el c\u00f3digo (hard-coded).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Por \u00faltimo, crea un nuevo archivo en <code>src\/services\/notifications.service.ts<\/code> y a\u00f1ade el siguiente c\u00f3digo:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    import { Job, Queue, Worker } from &quot;bullmq&quot;;rnimport {rn    QUEUE_NAMES,rn    SHIPPING_NOTICATIONS_QUEUE,rn    JOB_NAMES,rn} from &quot;..\/config\/queues.config&quot;;rnimport { redisConfig } from &quot;..\/config\/redis.config&quot;;rnimport { mgClient } from &quot;..\/config\/mailgun.config&quot;;rnimport { IMailgunClient } from &quot;mailgun.js\/Interfaces&quot;;rnimport { EmailTemplateManager } from &quot;..\/email-templates&quot;;rnimport { MailgunMessageData } from &quot;mailgun.js&quot;;rnimport { NotificationJob } from &quot;..\/types&quot;;rnrnexport class NotificationService {rn    private queue: Queue;rn    private mailgunClient: IMailgunClient;rn    private templateManager: EmailTemplateManager;rnrn    constructor() {rn        this.queue = SHIPPING_NOTICATIONS_QUEUE;rn        this.mailgunClient = mgClient;rn        this.templateManager = new EmailTemplateManager();rn        this.setupWorker();rn    }rnrn    private setupWorker() {rn        const worker = new Worker(rn            QUEUE_NAMES.SHIPPING_NOTICATIONS,rn            async (job) =&gt; {rn                console.log(`Processing job: ${job.id}`);rn                await this.processNotification(job);rn            },rn            {rn                connection: redisConfig,rn            }rn        );rnrn        worker.on(&quot;completed&quot;, (job) =&gt; {rn            console.log(`Job ${job.id} completed successfully`);rn        });rnrn        worker.on(&quot;failed&quot;, (job, error) =&gt; {rn            console.log(`Job ${job?.id} failed`, error);rn        });rn    }rnrn    private async processNotification(job: Job&lt;NotificationJob&gt;) {rn        const { email, trackingNumber, status, metadata } = job.data;rnrn        try {rn            const template = this.templateManager.getTemplate(status);rn            const emailData: MailgunMessageData = {rn                from: process.env.MAIL_FROM,rn                to: email,rn                subject: template.subject,rn                html: this.templateManager.renderTemplate(template.html, {rn                    trackingNumber,rn                    ...metadata,rn                }),rn            };rnrn            await this.mailgunClient.messages.create(rn                process.env.MAILGUN_DOMAIN,rn                emailDatarn            );rn        } catch (error) {rn            console.log(error);rn            throw error;rn        }rn    }rnrn    async queueNotification(data: NotificationJob) {rn        await this.queue.add(JOB_NAMES.SEND_NOTIFICATION, data, {rn            attempts: 5, \/\/ number of retry attempts before failing permanentlyrn            backoff: {rn                type: &quot;exponential&quot;, \/\/ Retry using exponential backoffrn                delay: 5000, \/\/ Delay in ms before retryingrn            },rn        });rn        console.log(&quot;Job queued successfully&quot;);rn    }rn}\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Este c\u00f3digo define una clase <code>NotificationService<\/code> que maneja notificaciones por email mediante un sistema de colas de trabajos con BullMQ y Mailgun. Esta clase inicializa tres propiedades privadas: queue, que es la cola donde se a\u00f1aden los trabajos de email; <code>mailgunClient<\/code>, que es el cliente de Mailgun para enviar emails; y <code>templateManager<\/code>, que es una instancia de la clase TemplateManager que creaste previamente para gestionar y recuperar plantillas de email. Esta clase tambi\u00e9n define los siguientes m\u00e9todos:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>setupWorker<\/code> crea un worker que escucha los nuevos trabajos de email y los procesa. Registra la finalizaci\u00f3n o el fallo del trabajo.<\/li>\n\n\n\n<li><code>processNotification<\/code> recupera la plantilla de email correcta, la rellena con datos din\u00e1micos y env\u00eda el email mediante Mailgun.<\/li>\n\n\n\n<li><code>queueNotification<\/code> a\u00f1ade un nuevo trabajo de email a la cola, con <a href=\"https:\/\/docs.bullmq.io\/guide\/retrying-failing-jobs\" target=\"_tabs\" rel=\"noopener noreferrer\">reintentos<\/a> y <a href=\"https:\/\/docs.bullmq.io\/guide\/retrying-failing-jobs#built-in-backoff-strategies\" target=\"_tabs\" rel=\"noopener noreferrer\">retroceso exponencial<\/a> para los intentos fallidos. Este m\u00e9todo se puede llamar desde otras partes de la aplicaci\u00f3n para a\u00f1adir trabajos a la cola.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Integraci\u00f3n con eventos de aplicaciones<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Para enviar notificaciones por email cuando se actualiza el estado de un pedido, tienes que poner en cola un trabajo en el sistema de notificaciones. Para ello, abre el archivo <code>src\/controllers\/order.controller.ts<\/code> y a\u00f1ade la siguiente declaraci\u00f3n de importaci\u00f3n para importar la clase <code>NotificationService<\/code>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>import { NotificationService } from \"..\/services\/notifications.service\";<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A continuaci\u00f3n, define una propiedad privada dentro de la clase <code>OrderController<\/code> para contener una instancia de la clase <code>NotificationService<\/code>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>private notificationService: NotificationService;<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Inicializa esta propiedad a\u00f1adiendo el siguiente c\u00f3digo dentro del m\u00e9todo <code>constructor<\/code>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>this.notificationService = new NotificationService();<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Por \u00faltimo, a\u00f1ade el siguiente c\u00f3digo dentro del m\u00e9todo <code>updateOrderStatus<\/code> despu\u00e9s de <code>await this.orderRepository.save(order);<\/code> para poner en cola un trabajo una vez que el estado del pedido se haya actualizado correctamente:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    \/\/ Send notificationrnthis.notificationService.queueNotification({rn    email: order.user.email,rn    trackingNumber: order.trackingNumber,rn    status,rn    metadata: {rn        name: order.user.name.split(&quot; &quot;)[0],rn    },rn});\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">El flujo de trabajo de emails transaccionales est\u00e1 ahora completo. Puedes seguir adelante y probar si todo funciona seg\u00fan lo esperado.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Probar el flujo de trabajo<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Para asegurarte de que todo funciona correctamente, inicia Redis ejecutando el siguiente comando en tu terminal:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>docker compose up -d<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Despu\u00e9s de ejecutar este comando, puedes comprobar que Redis est\u00e1 en funcionamiento enumerando los contenedores:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>docker ps<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Este comando muestra una lista de contenedores activos. Deber\u00edas ver que el contenedor <code>redis<\/code> se est\u00e1 ejecutando.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A continuaci\u00f3n, ejecuta el servidor Express con el comando <code>npm run dev<\/code> y dir\u00edgete a <a href=\"http:\/\/localhost:3000\/\" target=\"_tabs\" rel=\"noopener noreferrer\">http:\/\/localhost:3000<\/a> en tu navegador. En el panel de control de la aplicaci\u00f3n, actualiza el estado del pedido a cualquiera de las opciones disponibles. Una vez actualizado, recibir\u00e1s una respuesta instant\u00e1nea:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" class=\"wp-image-3955\" width=\"2012\" height=\"550\" src=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/6__4_.png\" alt=\"Shipped Order Status Screen Image\" srcset=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/6__4_.png 2012w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/6__4_-300x82.png 300w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/6__4_-768x210.png 768w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/6__4_-1024x280.png 1024w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/6__4_-1536x420.png 1536w\" sizes=\"(max-width: 2012px) 100vw, 2012px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">En tu terminal, aparecer\u00e1 un registro en el que se te notifica que el trabajo de env\u00edo de emails se ha puesto en cola con \u00e9xito y que se est\u00e1 procesando:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>Job queued successfully<br>\nProcessing job: 1<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Y cuando termine:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>Job 1 completed successfully<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A continuaci\u00f3n, recibir\u00e1s un email con el estado actualizado del pedido:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" class=\"wp-image-3958\" width=\"2028\" height=\"1024\" src=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/7__3_.png\" alt=\"Your order has shipped screen image\" srcset=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/7__3_.png 2028w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/7__3_-300x151.png 300w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/7__3_-768x388.png 768w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/7__3_-1024x517.png 1024w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/7__3_-1536x776.png 1536w\" sizes=\"(max-width: 2028px) 100vw, 2028px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Cada vez que actualices el estado de un pedido, el sistema enviar\u00e1 autom\u00e1ticamente el email correspondiente al usuario para mantenerlo informado:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" class=\"wp-image-3962\" width=\"2014\" height=\"986\" src=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/8__3_.png\" alt=\"Your Order has been delivered screen image 8\" srcset=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/8__3_.png 2014w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/8__3_-300x147.png 300w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/8__3_-768x376.png 768w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/8__3_-1024x501.png 1024w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/8__3_-1536x752.png 1536w\" sizes=\"(max-width: 2014px) 100vw, 2014px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">El c\u00f3digo completo de la aplicaci\u00f3n est\u00e1 disponible en <a href=\"https:\/\/github.com\/kimanikevin254\/mailgun-transactional-email-workflows\" target=\"_tabs\" rel=\"noopener noreferrer\">GitHub<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Seguimiento y gesti\u00f3n de emails<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Para garantizar que tus emails transaccionales llegan a los clientes de forma eficaz, tienes que supervisar el estado de la entrega y sus tasas de apertura, adem\u00e1s de gestionar cualquier posible fallo. Mailgun cuenta con un panel de control donde puedes hacer un seguimiento de los emails enviados, comprobar si se han entregado, abierto o si se ha hecho clic en ellos, as\u00ed como ver informes de rebotes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Puedes acceder a este panel de control en <b>Send &gt; Reporting &gt; Metrics<\/b> en Mailgun:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" class=\"wp-image-3968\" width=\"2016\" height=\"1072\" src=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/9__3_.png\" alt=\"Send &gt; Reporting &gt; Metrics Screen Image\" srcset=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/9__3_.png 2016w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/9__3_-300x160.png 300w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/9__3_-768x408.png 768w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/9__3_-1024x545.png 1024w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/9__3_-1536x817.png 1536w\" sizes=\"(max-width: 2016px) 100vw, 2016px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Sin embargo, las tasas de apertura no se rastrean de manera autom\u00e1tica, as\u00ed que tendr\u00e1s que configurarlas manualmente a trav\u00e9s de la pesta\u00f1a <b>Ajustes<\/b> de tu dominio de env\u00edo:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" class=\"wp-image-3975\" width=\"2040\" height=\"1064\" src=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/10__2_.png\" alt=\"Domain Settings Screen Tab image\" srcset=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/10__2_.png 2040w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/10__2_-300x156.png 300w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/10__2_-768x401.png 768w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/10__2_-1024x534.png 1024w, https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/10__2_-1536x801.png 1536w\" sizes=\"(max-width: 2040px) 100vw, 2040px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Tienes m\u00e1s informaci\u00f3n sobre c\u00f3mo rastrear las tasas de apertura y de clics en la <a href=\"https:\/\/documentation.mailgun.com\/docs\/mailgun\/user-manual\/tracking-messages\/#tracking-opens\" target=\"_tabs\" rel=\"noopener noreferrer\">documentaci\u00f3n oficial<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Adem\u00e1s, puedes usar <a href=\"https:\/\/documentation.mailgun.com\/docs\/mailgun\/user-manual\/tracking-messages\/#webhooks\" target=\"_tabs\" rel=\"noopener noreferrer\">webhooks<\/a> para recibir notificaciones en tiempo real sobre entregas fallidas y tomar la acci\u00f3n adecuada, como volver a enviar el email.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Al supervisar activamente el rendimiento de tus emails, puedes mejorar la entregabilidad, reducir los rebotes y asegurarte de que las notificaciones importantes llegan a tus clientes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">En resumen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">En este art\u00edculo has aprendido a configurar un flujo de trabajo de emails transaccionales para notificaciones de env\u00edo con Mailgun, BullMQ y Redis. Has aprendido a configurar Mailgun para enviar emails, definir una cola de trabajos con BullMQ e integrarla en un sistema de actualizaci\u00f3n de pedidos para que los clientes reciban notificaciones por email en tiempo real. Este enfoque mejora la comunicaci\u00f3n y perfecciona la experiencia del cliente en general.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u00bfTe ha resultado \u00fatil? Suscr\u00edbete a nuestra newsletter para disfrutar de m\u00e1s contenido de tutoriales y an\u00e1lisis en profundidad como este.<\/p>\n\n\n\r\n    <aside data-content-type=\"longform\"  class=\"banner-block longform-spacings rounded-lg shadow-lg px-5 py-6 px-md-6 py-md-7 p-lg-7 bg-light\" data-theme=\"dark\" aria-labelledby=\"banner-block-block_3fbf6f07a93f987f2445d12310427635\">\r\n                    <p class=\"text-uppercase section-caption text-body-color\">\r\n                Reg\u00edstrate            <\/p>\r\n                            <p class=\"h4 text-accent fw-bold\" id=\"banner-block-block_3fbf6f07a93f987f2445d12310427635\">\r\n                Empezar es facil\u00edsimo. Y gratis.            <\/p>\r\n                            <div class=\"mb-0 text-body-color\">\r\n                                    <div class=\"content-body mb-0 text-body-color\">Descubre todo lo que puedes lograr con la mejor plataforma de entrega de emails del mundo.<\/div>\r\n                            <\/div>\r\n        \r\n        <div class=\"text-start mt-5\"><a href=\"https:\/\/signup.mailgun.com\/new\/signup\"  class=\"btn btn-secondary\" >Get started<\/a><\/div>\r\n    <\/aside>\r\n\r\n","protected":false},"excerpt":{"rendered":"<p>Los clientes esperan recibir actualizaciones en tiempo real en el momento en que su pedido se pone en marcha. Esta gu\u00eda te explica c\u00f3mo crear un flujo de trabajo de emails transaccionales fiable para notificaciones de env\u00edo con la API de Mailgun y BullMQ, para que tus emails lleguen a la bandeja de entrada justo cuando deben.<\/p>\n","protected":false},"author":5,"featured_media":3952,"menu_order":0,"template":"","meta":{"_acf_changed":true,"footnotes":""},"blog_category":[19],"class_list":["post-19770","blog","type-blog","status-publish","has-post-thumbnail","hentry","blog_category-dev-life"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Crea flujos de trabajo de emails transaccionales con la API de Mailgun - Transactional Email API Service For Developers | Mailgun<\/title>\n<meta name=\"description\" content=\"Descubre c\u00f3mo crear emails de notificaci\u00f3n de env\u00edo escalables y basados en eventos con la API de Mailgun y BullMQ para obtener un mejor rendimiento y entregabilidad.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.mailgun.com\/es\/blog\/dev-life\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\/\" \/>\n<meta property=\"og:locale\" content=\"es_ES\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Crea flujos de trabajo de emails transaccionales con la API de Mailgun - Transactional Email API Service For Developers | Mailgun\" \/>\n<meta property=\"og:description\" content=\"Descubre c\u00f3mo crear emails de notificaci\u00f3n de env\u00edo escalables y basados en eventos con la API de Mailgun y BullMQ para obtener un mejor rendimiento y entregabilidad.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.mailgun.com\/es\/blog\/dev-life\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\/\" \/>\n<meta property=\"og:site_name\" content=\"Transactional Email API Service For Developers | Mailgun\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-14T15:17:59+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/MG-Blog-Dev-Life.png\" \/>\n\t<meta property=\"og:image:width\" content=\"720\" \/>\n\t<meta property=\"og:image:height\" content=\"448\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"11 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/dev-life\\\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\\\/\",\"url\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/dev-life\\\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\\\/\",\"name\":\"Crea flujos de trabajo de emails transaccionales con la API de Mailgun - Transactional Email API Service For Developers | Mailgun\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/dev-life\\\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/dev-life\\\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.mailgun.com\\\/wp-content\\\/uploads\\\/2025\\\/07\\\/MG-Blog-Dev-Life.png\",\"datePublished\":\"2025-06-11T00:00:00+00:00\",\"dateModified\":\"2026-09-14T15:17:59+00:00\",\"description\":\"Descubre c\u00f3mo crear emails de notificaci\u00f3n de env\u00edo escalables y basados en eventos con la API de Mailgun y BullMQ para obtener un mejor rendimiento y entregabilidad.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/dev-life\\\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\\\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/dev-life\\\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/dev-life\\\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.mailgun.com\\\/wp-content\\\/uploads\\\/2025\\\/07\\\/MG-Blog-Dev-Life.png\",\"contentUrl\":\"https:\\\/\\\/www.mailgun.com\\\/wp-content\\\/uploads\\\/2025\\\/07\\\/MG-Blog-Dev-Life.png\",\"width\":720,\"height\":448},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/dev-life\\\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Blog\",\"item\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Creaci\u00f3n de flujos de trabajo de emails transaccionales para notificaciones de env\u00edo con la API de Mailgun\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/#website\",\"url\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/\",\"name\":\"Transactional Email API Service For Developers | Mailgun\",\"description\":\"Potentes API de email transaccional que te permiten enviar, recibir y hacer seguimiento de emails, creadas pensando en los desarrolladores. \u00a1Descubre m\u00e1s!\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/#organization\",\"name\":\"Transactional Email API Service For Developers | Mailgun\",\"url\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.mailgun.com\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/cropped-android-chrome-512x512-1.png\",\"contentUrl\":\"https:\\\/\\\/www.mailgun.com\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/cropped-android-chrome-512x512-1.png\",\"width\":512,\"height\":512,\"caption\":\"Transactional Email API Service For Developers | Mailgun\"},\"image\":{\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Crea flujos de trabajo de emails transaccionales con la API de Mailgun - Transactional Email API Service For Developers | Mailgun","description":"Descubre c\u00f3mo crear emails de notificaci\u00f3n de env\u00edo escalables y basados en eventos con la API de Mailgun y BullMQ para obtener un mejor rendimiento y entregabilidad.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.mailgun.com\/es\/blog\/dev-life\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\/","og_locale":"es_ES","og_type":"article","og_title":"Crea flujos de trabajo de emails transaccionales con la API de Mailgun - Transactional Email API Service For Developers | Mailgun","og_description":"Descubre c\u00f3mo crear emails de notificaci\u00f3n de env\u00edo escalables y basados en eventos con la API de Mailgun y BullMQ para obtener un mejor rendimiento y entregabilidad.","og_url":"https:\/\/www.mailgun.com\/es\/blog\/dev-life\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\/","og_site_name":"Transactional Email API Service For Developers | Mailgun","article_modified_time":"2026-09-14T15:17:59+00:00","og_image":[{"width":720,"height":448,"url":"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/MG-Blog-Dev-Life.png","type":"image\/png"}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"11 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.mailgun.com\/es\/blog\/dev-life\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\/","url":"https:\/\/www.mailgun.com\/es\/blog\/dev-life\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\/","name":"Crea flujos de trabajo de emails transaccionales con la API de Mailgun - Transactional Email API Service For Developers | Mailgun","isPartOf":{"@id":"https:\/\/www.mailgun.com\/es\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.mailgun.com\/es\/blog\/dev-life\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\/#primaryimage"},"image":{"@id":"https:\/\/www.mailgun.com\/es\/blog\/dev-life\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\/#primaryimage"},"thumbnailUrl":"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/MG-Blog-Dev-Life.png","datePublished":"2025-06-11T00:00:00+00:00","dateModified":"2026-09-14T15:17:59+00:00","description":"Descubre c\u00f3mo crear emails de notificaci\u00f3n de env\u00edo escalables y basados en eventos con la API de Mailgun y BullMQ para obtener un mejor rendimiento y entregabilidad.","breadcrumb":{"@id":"https:\/\/www.mailgun.com\/es\/blog\/dev-life\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.mailgun.com\/es\/blog\/dev-life\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\/"]}]},{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.mailgun.com\/es\/blog\/dev-life\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\/#primaryimage","url":"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/MG-Blog-Dev-Life.png","contentUrl":"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/MG-Blog-Dev-Life.png","width":720,"height":448},{"@type":"BreadcrumbList","@id":"https:\/\/www.mailgun.com\/es\/blog\/dev-life\/como-crear-flujos-trabajo-emails-transaccionales-notificaciones-envio\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.mailgun.com\/es\/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https:\/\/www.mailgun.com\/es\/blog\/"},{"@type":"ListItem","position":3,"name":"Creaci\u00f3n de flujos de trabajo de emails transaccionales para notificaciones de env\u00edo con la API de Mailgun"}]},{"@type":"WebSite","@id":"https:\/\/www.mailgun.com\/es\/#website","url":"https:\/\/www.mailgun.com\/es\/","name":"Transactional Email API Service For Developers | Mailgun","description":"Potentes API de email transaccional que te permiten enviar, recibir y hacer seguimiento de emails, creadas pensando en los desarrolladores. \u00a1Descubre m\u00e1s!","publisher":{"@id":"https:\/\/www.mailgun.com\/es\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.mailgun.com\/es\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"},{"@type":"Organization","@id":"https:\/\/www.mailgun.com\/es\/#organization","name":"Transactional Email API Service For Developers | Mailgun","url":"https:\/\/www.mailgun.com\/es\/","logo":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.mailgun.com\/es\/#\/schema\/logo\/image\/","url":"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/06\/cropped-android-chrome-512x512-1.png","contentUrl":"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/06\/cropped-android-chrome-512x512-1.png","width":512,"height":512,"caption":"Transactional Email API Service For Developers | Mailgun"},"image":{"@id":"https:\/\/www.mailgun.com\/es\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/blog\/19770","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/blog"}],"about":[{"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/types\/blog"}],"author":[{"embeddable":true,"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/users\/5"}],"version-history":[{"count":1,"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/blog\/19770\/revisions"}],"predecessor-version":[{"id":19863,"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/blog\/19770\/revisions\/19863"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/media\/3952"}],"wp:attachment":[{"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/media?parent=19770"}],"wp:term":[{"taxonomy":"blog_category","embeddable":true,"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/blog_category?post=19770"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}