Skip to main content

Notification Providers

Notification providers handle sending notifications through various channels like email, SMS, push notifications, and more. They support both template-based and content-based notifications with attachments.

Available Notification Providers

Medusa includes two notification providers:

Local Notification Provider

@medusajs/medusa/notification-local - Logs notifications to the console. Use cases:
  • Development and testing
  • Debugging notification flows
  • Preview notification data

SendGrid Notification Provider

@medusajs/medusa/notification-sendgrid - Sends email notifications via SendGrid. Use cases:
  • Production email delivery
  • Transactional emails
  • Marketing emails
  • Template-based emails
Features:
  • Support for SendGrid templates
  • HTML email content
  • Email attachments
  • Dynamic template data

Installation

Both providers are included in the core Medusa package:
npm install @medusajs/medusa
For SendGrid, also install the SendGrid SDK:
npm install @sendgrid/mail

Configuration

Configure the local notification provider:
medusa-config.ts
import { defineConfig } from "@medusajs/framework/utils"

export default defineConfig({
modules: [
{
  resolve: "@medusajs/medusa/notification",
  options: {
    providers: [
      {
        resolve: "@medusajs/medusa/notification-local",
        id: "local",
        options: {},
      },
    ],
  },
},
],
})

Notification Provider Interface

All notification providers extend AbstractNotificationProviderService and implement the following method:

send(notification: ProviderSendNotificationDTO): Promise<ProviderSendNotificationResultsDTO>

Sends a notification. With HTML Content:
const result = await notificationProvider.send({
  to: "[email protected]",
  channel: "email",
  template: null,
  from: "[email protected]", // Optional, uses default from config
  content: {
    subject: "Order Confirmation",
    html: "<h1>Thank you for your order!</h1>",
  },
  data: {
    order_id: "order_123",
  },
  attachments: [
    {
      filename: "invoice.pdf",
      content: base64Content,
      content_type: "application/pdf",
    },
  ],
})
With SendGrid Template:
const result = await notificationProvider.send({
  to: "[email protected]",
  channel: "email",
  template: "d-1234567890abcdef", // SendGrid template ID
  data: {
    customer_name: "John Doe",
    order_number: "#12345",
    order_total: "$99.99",
  },
})
Parameters:
  • to - Recipient email address
  • channel - Notification channel (e.g., “email”, “sms”)
  • template - Template identifier (provider-specific)
  • from - Sender email (optional, uses default from config)
  • content - Notification content
    • subject - Email subject
    • html - HTML content
    • text - Plain text content (optional)
  • data - Dynamic data for templates
  • attachments - File attachments
    • filename - Attachment filename
    • content - Base64-encoded content
    • content_type - MIME type
    • disposition - “attachment” or “inline”
    • id - Content ID for inline images
SendGrid does not support mixing HTML content and templates. Use either content.html or template, not both.

Using the Notification Module

Access notification providers through the Notification Module:
import { Modules } from "@medusajs/framework/utils"

const notificationModule = container.resolve(Modules.NOTIFICATION)

// Send a notification
const notification = await notificationModule.createNotifications({
  to: "[email protected]",
  channel: "email",
  template: "order-confirmation",
  data: {
    order: orderData,
  },
  provider_id: "sendgrid",
})

Notification Workflows

Notifications are typically sent through workflows that handle specific events:
import { createWorkflow, WorkflowData } from "@medusajs/framework/workflows-sdk"
import { createNotificationStep } from "@medusajs/core-flows"

const sendOrderConfirmationWorkflow = createWorkflow(
  "send-order-confirmation",
  (input: WorkflowData<{ order_id: string }>) => {
    const notification = createNotificationStep({
      to: input.order.customer.email,
      channel: "email",
      template: "order-confirmation",
      data: {
        order_number: input.order.display_id,
        order_total: input.order.total,
        items: input.order.items,
      },
      provider_id: "sendgrid",
    })

    return notification
  }
)

Common Notification Types

Order Confirmation

await notificationModule.createNotifications({
  to: order.customer.email,
  channel: "email",
  template: "order-placed",
  data: {
    customer_name: order.customer.first_name,
    order_number: order.display_id,
    order_date: order.created_at,
    items: order.items,
    total: order.total,
    shipping_address: order.shipping_address,
  },
  provider_id: "sendgrid",
})

Shipment Notification

await notificationModule.createNotifications({
  to: order.customer.email,
  channel: "email",
  template: "order-shipped",
  data: {
    order_number: order.display_id,
    tracking_number: fulfillment.tracking_number,
    tracking_url: fulfillment.tracking_url,
    estimated_delivery: fulfillment.estimated_delivery,
  },
  provider_id: "sendgrid",
})

Password Reset

await notificationModule.createNotifications({
  to: user.email,
  channel: "email",
  template: "password-reset",
  data: {
    user_name: user.name,
    reset_token: resetToken,
    reset_url: `${process.env.STORE_URL}/reset-password?token=${resetToken}`,
  },
  provider_id: "sendgrid",
})

Creating Custom Notification Providers

Create a custom notification provider by extending AbstractNotificationProviderService:
packages/modules/providers/notification-custom/src/services/custom-notification.ts
import {
  AbstractNotificationProviderService,
  MedusaError,
} from "@medusajs/framework/utils"
import { Logger, NotificationTypes } from "@medusajs/framework/types"

type InjectedDependencies = {
  logger: Logger
}

interface CustomNotificationConfig {
  apiKey: string
  from: string
}

export class CustomNotificationService extends AbstractNotificationProviderService {
  static identifier = "custom-notification"
  protected config_: CustomNotificationConfig
  protected logger_: Logger
  protected client_: any

  constructor(
    { logger }: InjectedDependencies,
    options: CustomNotificationConfig
  ) {
    super()
    this.config_ = options
    this.logger_ = logger
    // Initialize your notification service client
    this.client_ = new NotificationServiceClient(options.apiKey)
  }

  async send(
    notification: NotificationTypes.ProviderSendNotificationDTO
  ): Promise<NotificationTypes.ProviderSendNotificationResultsDTO> {
    if (!notification) {
      throw new MedusaError(
        MedusaError.Types.INVALID_DATA,
        "No notification information provided"
      )
    }

    try {
      // Send via your notification service
      const result = await this.client_.send({
        to: notification.to,
        from: notification.from || this.config_.from,
        subject: notification.content?.subject,
        html: notification.content?.html,
        template: notification.template,
        data: notification.data,
        attachments: notification.attachments,
      })

      return {
        id: result.messageId,
      }
    } catch (error) {
      this.logger_.error(
        `Failed to send notification: ${error.message}`
      )
      throw new MedusaError(
        MedusaError.Types.UNEXPECTED_STATE,
        `Failed to send notification: ${error.message}`
      )
    }
  }
}

SMS Provider Example

packages/modules/providers/notification-sms/src/services/sms-notification.ts
import { AbstractNotificationProviderService } from "@medusajs/framework/utils"
import { Logger, NotificationTypes } from "@medusajs/framework/types"
import Twilio from "twilio"

export class SmsNotificationService extends AbstractNotificationProviderService {
  static identifier = "sms"
  protected client_: Twilio.Twilio
  protected from_: string

  constructor({ logger }, options) {
    super()
    this.client_ = Twilio(options.accountSid, options.authToken)
    this.from_ = options.from
  }

  async send(
    notification: NotificationTypes.ProviderSendNotificationDTO
  ): Promise<NotificationTypes.ProviderSendNotificationResultsDTO> {
    const message = await this.client_.messages.create({
      to: notification.to,
      from: this.from_,
      body: notification.content?.text || "",
    })

    return {
      id: message.sid,
    }
  }
}
Register your custom provider:
packages/modules/providers/notification-custom/src/index.ts
import { ModuleProvider, Modules } from "@medusajs/framework/utils"
import { CustomNotificationService } from "./services/custom-notification"

export default ModuleProvider(Modules.NOTIFICATION, {
  services: [CustomNotificationService],
})

Reference

  • Local Provider: packages/modules/providers/notification-local/src/services/local.ts
  • SendGrid Provider: packages/modules/providers/notification-sendgrid/src/services/sendgrid.ts
  • Base class: packages/core/utils/src/notification/abstract-notification-provider.ts
  • Types: packages/core/types/src/notification/provider.ts

Next Steps

Payment Providers

Send payment confirmation emails

Fulfillment Providers

Send shipment notifications

Build docs developers (and LLMs) love