Send Email Verification Notification

In this guide, you'll learn how to enable email verification requirement for customers in your Medusa application, and how to send email verification notifications.

Note: The steps of this guide have changed since Medusa v2.16.0. It now uses the http.authVerificationsPerActor configuration instead of the require_verification option in the Emailpass Auth Module Provider.

How Email Verification Works#

By default, customers can register an account and log in to your Medusa store without verifying their email address. You can enable email verification to require customers to verify their email address before they can log in.

When email verification is enabled, the customer registration flow changes to the following:

  1. A customer registers an account by providing their email and password.
  2. The customer receives an email with a verification link. The link points to a page in your storefront that verifies the customer's email when visited.
  3. The customer clicks the verification link, which verifies their email address and completes the registration process.
  4. The customer can now log in to their account.

This guide focuses on the backend implementation of this flow, which involves enabling email verification and sending the verification email.

To implement the storefront part of the flow, refer to the Verify Customer Account in Storefront guide.


Enable Email Verification#

To enable email verification, set the http.authVerificationsPerActor configuration in medusa-config.ts:

medusa-config.ts
1module.exports = defineConfig({2  // ...3  projectConfig: {4    // ...5    http: {6      authVerificationsPerActor: {7        customer: [8          {9            entity_type: "email",10            auth_provider: "emailpass",11          },12        ],13      },14    },15  },16})

The http.authVerificationsPerActor configuration is an object that specifies the required verification methods for each actor type. The value for each actor type is an array of objects, each specifying a verification method. In this case, you include the customer actor type and specify that email verification is required using the emailpass auth provider.


Send Email Verification Notification#

When the storefront requests to send an email verification notification, Medusa emits the auth.verification_requested event. You can listen to this event in a subscriber to send the email verification notification.

Prerequisite Configurations#

Notification Module Provider for Email Channel

To send the email verification notification, you need a Notification Module Provider configured for the email channel, such as SendGrid or Resend.

For development purposes, you can set the Notification Local Module Provider's channel to email in medusa-config.ts:

medusa-config.ts
1module.exports = defineConfig({2  // ...3  modules: [4    {5      resolve: "@medusajs/medusa/notification",6      options: {7        providers: [8          // ...9          {10            resolve: "@medusajs/medusa/notification-local",11            id: "local",12            options: {13              channels: ["email"],14            },15          },16        ],17      },18    },19  ],20})

This will log the email verification notification to the console when it's sent, instead of actually sending an email.

Storefront URL Configuration

To format the verification link in the email, you need to set the URL to your storefront in medusa-config.ts. You can use the admin.storefrontUrl configuration for that:

medusa-config.ts
1module.exports = defineConfig({2  // ...3  admin: {4    storefrontUrl: process.env.STOREFRONT_URL || "https://storefront.com",5  },6})

This will allow you to use the admin.storefrontUrl value in the subscriber when formatting the verification link. If admin.storefrontUrl is not set, you can use a placeholder URL as shown in the subscriber example below.

Email Verification Notification Subscriber#

To send the email verification notification when the auth.verification_requested event is emitted, create a subscriber at src/subscribers/send-email-verification.ts with the following content:

src/subscribers/send-email-verification.ts
1import {2  SubscriberArgs,3  type SubscriberConfig,4} from "@medusajs/medusa"5import { Modules } from "@medusajs/framework/utils"6
7export default async function verificationRequestedHandler({8  event: { data: {9    entity_id: email,10    entity_type,11    code,12  } },13  container,14}: SubscriberArgs<{15  entity_id: string16  entity_type: string17  code_provider: string18  auth_identity_id: string19  code: string20  expires_at: string21  metadata?: Record<string, unknown>22}>) {23  // only handle email verifications.24  if (entity_type !== "email") {25    return26  }27
28  const notificationModuleService = container.resolve(29    Modules.NOTIFICATION30  )31  const config = container.resolve("configModule")32
33  const urlPrefix = config.admin.storefrontUrl || "https://storefront.com"34
35  console.log(`${urlPrefix}/verify-account?token=${code}&email=${email}`)36
37  await notificationModuleService.createNotifications({38    to: email,39    channel: "email",40    // TODO replace with template ID in notification provider41    template: "email-verification",42    data: {43      // a URL to a frontend application44      verification_url: `${urlPrefix}/verify-account?token=${code}&email=${email}`,45    },46  })47}48
49export const config: SubscriberConfig = {50  event: "auth.verification_requested",51}

Event Payload

This subscriber receives in the event payload an object with the following properties:

  • entity_id: The identifier of the provider identity to verify. For email verification, this is the customer's email address.
  • entity_type: The kind of entity being verified, such as email. You check that it's email before sending the notification, since you only want to handle email verifications.
  • code: The verification code to include in the verification link. When the customer clicks the verification link, the storefront should send the code back to the Medusa application to verify the customer's email address.
  • code_provider: The verification provider used to generate the code. By default, this is token.
  • auth_identity_id: The ID of the auth identity for which the verification was requested.
  • expires_at: The date and time at which the verification code expires.
  • metadata: Optional metadata included in the verification request.

Subscriber Implementation

In the subscriber, you first check that the entity_type is email to only handle email verifications. Then, you format the storefront URL either using the value of admin.storefrontUrl set in medusa-config.ts or a placeholder URL. You need this URL to format the verification link sent in the email.

Then, you send a notification using the Notification Module's main service. You pass the following properties to the createNotifications method:

  • to: The email address of the customer to which the verification email should be sent.
  • channel: The channel to send the notification through. In this case, it's email.
  • template: The ID of the template to use for the notification. This depends on your Notification Module Provider. For example, if you're using the SendGrid Notification Module Provider, this should be the ID of a SendGrid template that you've set up to format the email verification notification.
  • data: The data to be passed to the template when sending the notification. In this case, you pass the verification_url property, which contains the verification link to be included in the email.

The verification link points to a page in your storefront, such as /verify-account, which should handle verifying the customer's email when visited.


Handle Email Verification in Storefront#

When the customer clicks the verification link, they should be directed to a page in your storefront that sends the token back to the Medusa application to verify the customer's email address.

To learn how to implement this part of the flow, refer to the Verify Customer Account in Storefront guide.


Example Notification Templates#

The following section provides example notification templates for some Notification Module Providers.

SendGrid#

Note: Refer to the SendGrid Notification Module Provider documentation for more details on how to set up SendGrid.

The following HTML template can be used with SendGrid to send an email verification email:

Code
1<!DOCTYPE html>2<html>3<head>4    <meta charset="utf-8">5    <meta name="viewport" content="width=device-width, initial-scale=1.0">6    <title>Verify Your Email</title>7    <style>8        body {9            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;10            background-color: #ffffff;11            margin: 0;12            padding: 20px;13        }14        .container {15            max-width: 465px;16            margin: 40px auto;17            border: 1px solid #eaeaea;18            border-radius: 5px;19            padding: 20px;20        }21        .header {22            text-align: center;23            margin: 30px 0;24        }25        .title {26            color: #000000;27            font-size: 24px;28            font-weight: normal;29            margin: 0;30        }31        .content {32            margin: 32px 0;33        }34        .text {35            color: #000000;36            font-size: 14px;37            line-height: 24px;38            margin: 0 0 16px 0;39        }40        .button-container {41            text-align: center;42            margin: 32px 0;43        }44        .verify-button {45            background-color: #000000;46            border-radius: 3px;47            color: #ffffff;48            font-size: 12px;49            font-weight: 600;50            text-decoration: none;51            text-align: center;52            padding: 12px 20px;53            display: inline-block;54        }55        .verify-button:hover {56            background-color: #333333;57        }58        .url-section {59            margin: 32px 0;60        }61        .url-link {62            color: #2563eb;63            text-decoration: none;64            font-size: 14px;65            line-height: 24px;66            word-break: break-all;67        }68        .disclaimer {69            margin: 32px 0;70        }71        .disclaimer-text {72            color: #666666;73            font-size: 12px;74            line-height: 24px;75            margin: 0 0 8px 0;76        }77        .security-footer {78            margin-top: 32px;79            padding-top: 20px;80            border-top: 1px solid #eaeaea;81        }82        .security-text {83            color: #666666;84            font-size: 12px;85            line-height: 24px;86            margin: 0;87        }88    </style>89</head>90<body>91    <div class="container">92        <div class="header">93            <h1 class="title">Verify Your Email</h1>94        </div>95
96        <div class="content">97            <p class="text">98                Hello,99            </p>100            <p class="text">101                Thanks for creating an account. Please confirm your email address by clicking the button below to complete your registration.102            </p>103        </div>104
105        <div class="button-container">106            <a href="{{verification_url}}" class="verify-button">107                Verify Email108            </a>109        </div>110
111        <div class="url-section">112            <p class="text">113                Or copy and paste this URL into your browser:114            </p>115            <a href="{{verification_url}}" class="url-link">116                {{verification_url}}117            </a>118        </div>119
120        <div class="disclaimer">121            <p class="disclaimer-text">122                This verification link will expire soon for security reasons.123            </p>124            <p class="disclaimer-text">125                If you didn't create an account, you can safely ignore this email.126            </p>127        </div>128
129        <div class="security-footer">130            <p class="security-text">131                For security reasons, never share this verification link with anyone. If you're having trouble with the button above, copy and paste the URL into your web browser.132            </p>133        </div>134    </div>135</body>136</html>

Make sure to pass the verification_url variable to the template, which contains the URL to verify the customer's email.

You can also customize the template further to show other information.

Resend#

If you've integrated Resend as explained in the Resend Integration Guide, you can add a new template for email verification at src/modules/resend/emails/email-verification.tsx:

src/modules/resend/emails/email-verification.tsx
1import { 2  Text, 3  Container, 4  Heading, 5  Html, 6  Section, 7  Tailwind, 8  Head, 9  Preview, 10  Body, 11  Link,12  Button, 13} from "@react-email/components"14
15type EmailVerificationEmailProps = {16  verification_url: string17}18
19function EmailVerificationEmailComponent({ verification_url }: EmailVerificationEmailProps) {20  return (21    <Html>22      <Head />23      <Preview>Verify your email</Preview>24      <Tailwind>25        <Body className="bg-white my-auto mx-auto font-sans px-2">26          <Container className="border border-solid border-[#eaeaea] rounded my-[40px] mx-auto p-[20px] max-w-[465px]">27            <Section className="mt-[32px]">28              <Heading className="text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0">29                Verify Your Email30              </Heading>31            </Section>32
33            <Section className="my-[32px]">34              <Text className="text-black text-[14px] leading-[24px]">35                Hello,36              </Text>37              <Text className="text-black text-[14px] leading-[24px]">38                Thanks for creating an account. Please confirm your email address by clicking the button below to complete your registration.39              </Text>40            </Section>41
42            <Section className="text-center mt-[32px] mb-[32px]">43              <Button44                className="bg-[#000000] rounded text-white text-[12px] font-semibold no-underline text-center px-5 py-3"45                href={verification_url}46              >47                Verify Email48              </Button>49            </Section>50
51            <Section className="my-[32px]">52              <Text className="text-black text-[14px] leading-[24px]">53                Or copy and paste this URL into your browser:54              </Text>55              <Link56                href={verification_url}57                className="text-blue-600 no-underline text-[14px] leading-[24px] break-all"58              >59                {verification_url}60              </Link>61            </Section>62
63            <Section className="my-[32px]">64              <Text className="text-[#666666] text-[12px] leading-[24px]">65                This verification link will expire soon for security reasons.66              </Text>67              <Text className="text-[#666666] text-[12px] leading-[24px] mt-2">68                If you didn't create an account, you can safely ignore this email.69              </Text>70            </Section>71
72            <Section className="mt-[32px] pt-[20px] border-t border-solid border-[#eaeaea]">73              <Text className="text-[#666666] text-[12px] leading-[24px]">74                For security reasons, never share this verification link with anyone. If you're having trouble with the button above, copy and paste the URL into your web browser.75              </Text>76            </Section>77          </Container>78        </Body>79      </Tailwind>80    </Html>81  )82}83
84export const emailVerificationEmail = (props: EmailVerificationEmailProps) => (85  <EmailVerificationEmailComponent {...props} />86)87
88// Mock data for preview/development89const mockEmailVerification: EmailVerificationEmailProps = {90  verification_url: "https://your-app.com/verify-account?token=sample-verification-token-123&email=user@example.com",91}92
93export default () => <EmailVerificationEmailComponent {...mockEmailVerification} />

Feel free to customize the email template further to match your branding and style, or to add additional information.

Then, in the Resend Module's service at src/modules/resend/service.ts, add the new template to the templates object and Templates type:

src/modules/resend/service.ts
1// other imports...2import { emailVerificationEmail } from "./emails/email-verification"3
4enum Templates {5  // ...6  EMAIL_VERIFICATION = "email-verification",7}8
9const templates: {[key in Templates]?: (props: unknown) => React.ReactNode} = {10  // ...11  [Templates.EMAIL_VERIFICATION]: emailVerificationEmail,12}

Finally, find the getTemplateSubject function in the ResendNotificationProviderService and add a case for the EMAIL_VERIFICATION template:

src/modules/resend/service.ts
1class ResendNotificationProviderService extends AbstractNotificationProviderService {2  // ...3
4  private getTemplateSubject(template: Templates) {5    // ...6    switch (template) {7      // ...8      case Templates.EMAIL_VERIFICATION:9        return "Verify Your Email"10    }11  }12}
Was this page helpful?
Ask Bloom
For assistance in your development, use Claude Code Plugins or Medusa MCP server in Cursor, VSCode, etc...FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break