Skip to main content

Sending Email Notifications

Once configured, you can send email notifications using the Notification Module.

Simple Text Email

import { Modules } from "@medusajs/framework/utils"

// Inside an API route, workflow, or subscriber
const notificationService = container.resolve(Modules.NOTIFICATION)

await notificationService.createNotifications({
  to: "customer@example.com",
  channel: "email",
  template: "order-confirmation", // ℹ️ This is a mandatory field for Medusa, but not used inside the Nodemailer provider
  content: {
    subject: "Order Confirmation",
    text: "Thank you for your order! Your order #12345 has been confirmed."
  }
})

HTML Email

await notificationService.createNotifications({
  to: "customer@example.com",
  template: "welcome",
  channel: "email",
  content: {
    subject: "Welcome to Our Store",
    html: `
      <h1>Welcome!</h1>
      <p>Thank you for creating an account.</p>
      <a href="https://example.com/shop">Start Shopping</a>
    `
  }
})

Common Use Cases

Custom From Address

You can override the default from address for specific emails:
await notificationService.createNotifications({
  from: "support@example.com", // Override default from
  to: "customer@example.com",
  channel: "email",
  template: "support-response",
  content: {
    subject: "Support Response",
    text: "We've received your support request..."
  }
})

Email with Attachments

You can attach files to emails. The plugin supports both regular attachments and inline images.

Regular Attachment

import QRCode from "qrcode"

const qrBuffer = await QRCode.toBuffer("VOUCHER-123")

// Convert to binary string (Medusa's recommended format)
const binaryString = [...qrBuffer]
  .map((byte) => byte.toString(2).padStart(8, "0"))
  .join("")

await notificationService.createNotifications({
  to: "customer@example.com",
  channel: "email",
  template: "voucher",
  content: {
    subject: "Your Voucher",
    html: "<p>Please find your QR code attached.</p>"
  },
  attachments: [
    {
      content: binaryString,
      filename: "voucher-qr.png",
      content_type: "image/png",
      disposition: "attachment",
    }
  ]
})

Inline Image (CID)

To embed an image directly in the email body, use the disposition: "inline" option with an id (Content-ID) that you reference in your HTML:
import QRCode from "qrcode"

const dataUrl = await QRCode.toDataURL("VOUCHER-123")
// Strip the data URL prefix to get pure base64
const base64Content = dataUrl.replace(/^data:image\/png;base64,/, "")

await notificationService.createNotifications({
  to: "customer@example.com",
  channel: "email",
  template: "voucher",
  content: {
    subject: "Your Voucher",
    html: `
      <h1>Your Voucher</h1>
      <p>Scan this QR code:</p>
      <img src="cid:qr-code" alt="QR Code" width="200" height="200" />
    `
  },
  attachments: [
    {
      id: "qr-code",           // Content-ID for referencing in HTML
      content: base64Content,  // base64 string
      filename: "qr-code.png",
      content_type: "image/png",
      disposition: "inline",   // Embed in email body
    }
  ]
})
The id field maps to the Content-ID (CID) header. Reference it in your HTML with src="cid:<id>".