diff --git a/www/apps/book/public/llms-full.txt b/www/apps/book/public/llms-full.txt index e099807355..7c5542bb15 100644 --- a/www/apps/book/public/llms-full.txt +++ b/www/apps/book/public/llms-full.txt @@ -18721,6 +18721,7 @@ This documentation is split into the following sections: |API Routes References|References of the | |References|Useful during your development with Medusa to learn about different APIs and how to use them. Its references include the | |User Guide|Guides that introduce merchants and store managers to the Medusa Admin dashboard and helps them understand how to use the dashboard to manage their store.| +|Cloud|Learn about Cloud, our managed services offering for Medusa applications. Find guides on how to deploy your Medusa application, manage organizations, and more.| To get started, check out the [Installation chapter](https://docs.medusajs.com/learn/installation/index.html.md). @@ -59141,6 +59142,1273 @@ If you're new to Medusa, check out the [main documentation](https://docs.medusaj To learn more about the commerce features that Medusa provides, check out Medusa's [Commerce Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md). +# Add Newsletter Subscriptions with Mailchimp in Medusa + +In this tutorial, you'll learn how to integrate Mailchimp with Medusa to manage newsletter subscribers and automate newsletters. + +When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. Medusa's architecture facilitates integrating third-party services to customize Medusa's infrastructure for your business needs. + +Medusa's [Notification Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/notification/index.html.md) allows you to customize Medusa's infrastructure to send notifications using the third-party provider that fits your business needs, such as [Mailchimp](https://mailchimp.com/). + +In this tutorial, you'll integrate Mailchimp with Medusa to allow customers to subscribe to your newsletter and automate sending newsletters. + +## Summary + +By following this tutorial, you'll learn how to: + +- Install and set up Medusa. +- Integrate Mailchimp with Medusa. +- Allow customers to subscribe to your store's newsletter. +- Automate sending newsletters about new products to subscribed customers. + +You can follow this tutorial whether you're new to Medusa or an advanced Medusa developer. + +![Diagram showcasing the flow of the Mailchimp integration with Medusa](https://res.cloudinary.com/dza7lstvk/image/upload/v1750679344/Medusa%20Resources/mailchimp-overview_itmiul.jpg) + +[Example Repository](https://github.com/medusajs/examples/tree/main/mailchimp-integration): Find the full code of the guide in this repository. + +*** + +## Step 1: Install a Medusa Application + +### Prerequisites + +- [Node.js v20+](https://nodejs.org/en/download) +- [Git CLI tool](https://git-scm.com/downloads) +- [PostgreSQL](https://www.postgresql.org/download/) + +Start by installing the Medusa application on your machine with the following command: + +```bash +npx create-medusa-app@latest +``` + +First, you'll be asked for the project's name. Then, when prompted about installing the [Next.js Starter Storefront](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/index.html.md), choose "Yes." + +Afterwards, the installation process will start, which will install the Medusa application in a directory with your project's name and the Next.js Starter Storefront in a separate directory named `{project-name}-storefront`. + +The Medusa application is composed of a headless Node.js server and an admin dashboard. The storefront is installed or custom-built separately and connects to the Medusa application through its REST endpoints, called [API routes](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md). Learn more in [Medusa's Architecture documentation](https://docs.medusajs.com/docs/learn/introduction/architecture/index.html.md). + +Once the installation finishes successfully, the Medusa Admin dashboard will open with a form to create a new user. Enter the user's credentials and submit the form. Afterwards, you can log in with the new user and explore the dashboard. + +Check out the [troubleshooting guides](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/troubleshooting/create-medusa-app-errors/index.html.md) for help. + +*** + +## Step 2: Create Mailchimp Module Provider + +To integrate third-party services into Medusa, you create a custom [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md). A module is a reusable package with functionalities related to a single feature or domain. + +Medusa's [Notification Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/notification/index.html.md) provides an interface to send notifications in your Medusa application. It delegates the actual sending of notifications to the underlying provider, such as Mailchimp. + +In this step, you'll integrate Mailchimp as a Notification Module Provider. Later, you'll use it to handle newsletter subscriptions and send newsletters. + +Refer to the [Modules](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) documentation to learn more about modules in Medusa. + +### a. Install Mailchimp Marketing API SDK + +To interact with Mailchimp's APIs, you'll use their official Node.js SDK. + +Run the following command to install the SDK with its types package in your Medusa application: + +```bash npm2yarn +npm install @mailchimp/mailchimp_marketing +npm install @types/mailchimp__mailchimp_marketing --save-dev +``` + +### b. Create Module Directory + +A module is created under the `src/modules` directory of your Medusa application. So, create the directory `src/modules/mailchimp`. + +### c. Create Mailchimp Module's Service + +A module has a service that contains its logic. For Notification Module Providers, the service implements the logic to send notifications with a third-party service. + +To create the service of the Mailchimp Notification Module Provider, create the file `src/modules/mailchimp/service.ts` with the following content: + +```ts title="src/modules/mailchimp/service.ts" highlights={serviceHighlights} +import { + AbstractNotificationProviderService, +} from "@medusajs/framework/utils" +import mailchimpMarketingApi from "@mailchimp/mailchimp_marketing" + +type Options = { + apiKey: string + server: string + listId: string + templates?: { + new_products?: { + subject_line?: string + storefront_url?: string + } + } +} + +type InjectedDependencies = { +} + +class MailchimpNotificationProviderService extends AbstractNotificationProviderService { + static identifier = "mailchimp" + protected options: Options + protected mailchimp: typeof mailchimpMarketingApi + + constructor(container: InjectedDependencies, options: Options) { + super() + this.options = options + this.mailchimp = mailchimpMarketingApi + this.mailchimp.setConfig({ + apiKey: options.apiKey, + server: options.server, + }) + } +} + +export default MailchimpNotificationProviderService +``` + +A Notification Module Provider's service must extend the `AbstractNotificationProviderService` class. You'll implement its methods in the next sections. + +The service must also have an `identifier` static property, which is a unique identifier for the module. This identifier is used when registering the module in the Medusa application. + +The service's constructor receives two parameters: + +- `container`: The [module's container](https://docs.medusajs.com/docs/learn/fundamentals/modules/container/index.html.md) that contains Framework resources available to the module. You don't need to use it for this tutorial. +- `options`: Options that are passed to the module provider when it's registered in Medusa's configurations. You define the following options: + - `apiKey`: The Mailchimp API key. + - `server`: The Mailchimp server prefix. For example, `us10`. + - `listId`: The ID of the Mailchimp audience list where subscribed customers will be added. + - `templates`: Optional template configurations for newsletters. + +You'll learn how to set these options in the [Add Module Provider to Medusa's Configurations](#g-add-module-provider-to-medusas-configurations) section. + +In the constructor, you set the `options` property and initialize the Mailchimp SDK with your API key and server. + +In the next sections, you'll implement the methods of the `MailchimpNotificationProviderService` class. + +### d. Implement validateOptions Method + +The `validateOptions` method is used to validate the options passed to the module provider. If the method throws an error, the Medusa application won't start. + +Add the `validateOptions` method to the `MailchimpNotificationProviderService` class: + +```ts title="src/modules/mailchimp/service.ts" +// other imports... +import { + MedusaError, +} from "@medusajs/framework/utils" + +class MailchimpNotificationProviderService extends AbstractNotificationProviderService { + // ... + static validateOptions(options: Record): void | never { + if (!options.apiKey) { + throw new MedusaError( + MedusaError.Types.INVALID_ARGUMENT, + "API key is required" + ) + } + if (!options.server) { + throw new MedusaError( + MedusaError.Types.INVALID_ARGUMENT, + "Server is required" + ) + } + if (!options.listId) { + throw new MedusaError( + MedusaError.Types.INVALID_ARGUMENT, + "List ID is required" + ) + } + } +} +``` + +The `validateOptions` method receives the options passed to the module provider as a parameter. + +In the method, you throw an error if the required options are not set. + +### e. Implement send Method + +When the Medusa application needs to send a notification through a channel (such as `email`), it calls the `send` method of the channel's module provider. + +The `send` method can be used to send different types of notifications based on the template specified. So, you'll implement the helper methods that handle the different notification templates, then use them in the `send` method. + +#### sendNewsletterSignup Method + +The `sendNewsletterSignup` method adds an email to the Mailchimp audience list. You'll use this method when a customer subscribes to the newsletter. + +Add the `sendNewsletterSignup` method to the `MailchimpNotificationProviderService` class: + +```ts title="src/modules/mailchimp/service.ts" +// other imports... +import { + ProviderSendNotificationResultsDTO, + ProviderSendNotificationDTO, +} from "@medusajs/framework/types" + +class MailchimpNotificationProviderService extends AbstractNotificationProviderService { + // ... + async sendNewsletterSignup( + notification: ProviderSendNotificationDTO + ): Promise { + const { to, data } = notification + + try { + const response = await this.mailchimp.lists.addListMember( + this.options.listId, { + email_address: to, + status: "subscribed", + merge_fields: { + FNAME: data?.first_name, + LNAME: data?.last_name, + }, + } + ) + + return { + id: "id" in response ? response.id : "", + } + } catch (error) { + throw new MedusaError( + MedusaError.Types.UNEXPECTED_STATE, + `Failed to send newsletter signup: ${error.response.text}` + ) + } + } +} +``` + +This method receives the same parameter as the `send` method, which is an object containing the notification details including: + +- `to`: The email address to add to the Mailchimp audience list. +- `data`: An object containing additional data, such as the user's first and last name. + +Learn about other properties in the object in the [Create Notification Module Provider](https://docs.medusajs.com/references/notification-provider-module#send/index.html.md) guide. + +In the method, you use the `mailchimp.lists.addListMember` method to subscribe an email address to the Mailchimp audience list. You pass the `listId` from the module's options and the email address along with optional first and last names. + +If the subscription is successful, the method returns an object with the `id` of the subscribed email. If it fails, it throws a `MedusaError` with the error message from Mailchimp. + +You don't do any email sending in this method because later, in the [Send Welcome Emails from Mailchimp](#optional-step-send-welcome-emails-from-mailchimp) section, you'll create an automation flow in Mailchimp that automatically sends a welcome email to new subscribers. + +#### getNewProductsHtmlContent Method + +The `getNewProductsHtmlContent` method will generate the HTML content for the "New Products" newsletter. You'll then use this method when sending the new products newsletter. + +Add the `getNewProductsHtmlContent` method to the `MailchimpNotificationProviderService` class: + +```ts title="src/modules/mailchimp/service.ts" +class MailchimpNotificationProviderService extends AbstractNotificationProviderService { + // ... + private async getNewProductsHtmlContent(data: any): Promise { + return ` + + + + + ${this.options.templates?.new_products?.subject_line} + + + +
+

Check out our latest products

+ + + + + ${data.products.map((product: any) => ` + + + + `).join("")} + +
+ Product Image +
+

${product.title}

+

${product.description}

+ View Product +
+
+ +
+ + + ` + } +} +``` + +This method receives the product data as a parameter and returns a string containing the HTML content for the newsletter. You show the products in a responsive layout with images, titles, descriptions, and a button to view the product. + +Notice that the HTML template uses the `template.new_products.storefront_url` module option to generate the product links. This allows you to customize the storefront URL in the module's options. + +Feel free to modify the HTML template to match your design preferences or add more product details. You can also define a template in Mailchimp and use its ID in the next method instead of generating the HTML content dynamically. + +#### sendNewProducts Method + +The last helper method you'll add is `sendNewProducts`. This method will create and send a campaign in Mailchimp that showcases new products. + +Add the `sendNewProducts` method to the `MailchimpNotificationProviderService` class: + +```ts title="src/modules/mailchimp/service.ts" highlights={sendNewProductsHighlights} +class MailchimpNotificationProviderService extends AbstractNotificationProviderService { + // ... + async sendNewProducts( + notification: ProviderSendNotificationDTO + ): Promise { + const { data } = notification + + try { + const list = await fetch( + `https://${this.options.server}.api.mailchimp.com/3.0/lists/${this.options.listId}`, + { + headers: { + Authorization: `Bearer ${this.options.apiKey}`, + }, + } + ).then((res) => res.json()) as mailchimpMarketingApi.lists.List + + // create a campaign + const campaign = await this.mailchimp.campaigns.create({ + type: "regular", + recipients: { + list_id: this.options.listId, + }, + settings: { + subject_line: + this.options.templates?.new_products?.subject_line || "New Products", + from_name: list.campaign_defaults.from_name, + reply_to: list.campaign_defaults.from_email, + }, + }) as mailchimpMarketingApi.campaigns.Campaigns + + // set content + await this.mailchimp.campaigns.setContent(campaign.id, { + html: await this.getNewProductsHtmlContent(data), + }) + + // send campaign + await this.mailchimp.campaigns.send(campaign.id) + + return { + id: campaign.id, + } + } catch (error) { + throw new MedusaError( + MedusaError.Types.UNEXPECTED_STATE, + `Failed to send new products newsletter: ${ + error.response?.text || error + }` + ) + } + } +} +``` + +This method receives the same parameter as the `send` method, which is an object containing the notification details. + +Learn about the object's properties in the [Create Notification Module Provider](https://docs.medusajs.com/references/notification-provider-module#send/index.html.md) guide. + +In the method, you: + +1. Fetch the Mailchimp list details to get default sender information. You use the `fetch` API because the Mailchimp SDK does not provide a method to fetch list details. +2. Create a new campaign in Mailchimp using the `mailchimp.campaigns.create` method. You specify the list ID to ensure the subscribers of that list receive the campaign. You also set the subject line and sender information using the list's default settings. +3. Set the campaign content using the HTML returned by the `getNewProductsHtmlContent` method. +4. Send the campaign using the `mailchimp.campaigns.send` method. +5. Return the campaign ID if successful. + +You also throw a `MedusaError` if the method fails at any point, providing the error message from Mailchimp. + +#### Implement send Method + +You can now implement the required `send` method of the `MailchimpNotificationProviderService` class that sends a notification based on the template provided. + +Add the `send` method to the `MailchimpNotificationProviderService` class: + +```ts title="src/modules/mailchimp/service.ts" +class MailchimpNotificationProviderService extends AbstractNotificationProviderService { + // ... + async send( + notification: ProviderSendNotificationDTO + ): Promise { + const { template } = notification + + switch (template) { + case "newsletter-signup": + return this.sendNewsletterSignup(notification) + case "new-products": + return this.sendNewProducts(notification) + default: + throw new MedusaError( + MedusaError.Types.INVALID_ARGUMENT, + "Invalid template" + ) + } + } +} +``` + +This method receives an object of notification details, including the `template` property that specifies which template to use for sending the notification. + +Learn about other properties in the object in the [Create Notification Module Provider](https://docs.medusajs.com/references/notification-provider-module#send/index.html.md) guide. + +In the method, you perform an action based on the `template` value: + +- If the template is `newsletter-signup`, you call the `sendNewsletterSignup` method to add a new subscriber to the Mailchimp audience list. +- If the template is `new-products`, you call the `sendNewProducts` method to create and send a campaign showcasing new products. +- If the template is not recognized, you throw a `MedusaError` indicating that the template is invalid. + +### f. Export Module Definition + +You've now finished implementing the necessary methods for the Mailchimp Notification Module Provider. + +The final piece to a module is its definition, which you export in an `index.ts` file at the module's root directory. This definition tells Medusa the module's details, including its service. + +To create the module's definition, create the file `src/modules/mailchimp/index.ts` with the following content: + +```ts title="src/modules/mailchimp/index.ts" +import MailchimpNotificationProviderService from "./service" +import { + ModuleProvider, + Modules, +} from "@medusajs/framework/utils" + +export default ModuleProvider(Modules.NOTIFICATION, { + services: [MailchimpNotificationProviderService], +}) +``` + +You use `ModuleProvider` from the Modules SDK to create the module provider's definition. It accepts two parameters: + +1. The name of the module that this provider belongs to, which is `Modules.NOTIFICATION` in this case. +2. An object with a required property `services` indicating the Module Provider's services. + +### g. Add Module Provider to Medusa's Configurations + +Once you finish building the module, add it to Medusa's configurations to start using it. + +In `medusa-config.ts`, add a `modules` property to the configurations: + +```ts title="medusa-config.ts" highlights={configurationsHighlight} +module.exports = defineConfig({ + // ... + modules: [ + { + resolve: "@medusajs/medusa/notification", + options: { + providers: [ + { + resolve: "./src/modules/mailchimp", + id: "mailchimp", + options: { + channels: ["email"], + apiKey: process.env.MAILCHIMP_API_KEY!, + server: process.env.MAILCHIMP_SERVER!, + listId: process.env.MAILCHIMP_LIST_ID!, + templates: { + new_products: { + subject_line: process.env.MAILCHIMP_NEW_PRODUCTS_SUBJECT_LINE!, + storefront_url: process.env.MAILCHIMP_NEW_PRODUCTS_STOREFRONT_URL!, + }, + }, + }, + }, + ], + }, + }, + ], +}) +``` + +To pass a Module Provider to the Notification Module, you add the `modules` property to the Medusa configuration and pass the Notification Module in its value. + +The Notification Module accepts a `providers` option, which is an array of Notification Module Providers to register. + +To register the Mailchimp Notification Module Provider, you add an object to the `providers` array with the following properties: + +- `resolve`: The NPM package or path to the module provider. In this case, it's the path to the `src/modules/mailchimp` directory. +- `id`: The ID of the module provider. The Notification Module Provider is then registered with the ID `np_{identifier}_{id}`, where: + - `{identifier}`: The identifier static property defined in the Module Provider's service, which is `mailchimp` in this case. + - `{id}`: The ID set in this configuration, which is also `mailchimp` in this case. +- `options`: The options to pass to the module provider. These are the options you defined in the `Options` type of the module provider's service. + - You must also set a `channels` option that indicates the channels this provider is used to send notifications. + +### h. Set Environment Variables + +Next, you'll set the options you passed to the Mailchimp Notification Module Provider as environment variables. + +#### Retrieve Mailchimp API Key + +To retrieve your Mailchimp API key: + +1. On the Mailchimp dashboard, click on your profile icon at the top right. +2. Choose "Account & billing" from the dropdown. + +![Mailchimp dashboard with the profile menu opened](https://res.cloudinary.com/dza7lstvk/image/upload/v1750682692/Medusa%20Resources/CleanShot_2025-06-23_at_11.46.07_2x_xo5a5l.png) + +3. Click on the "Extras" tab and select "API keys" from the dropdown. + +![Mailchimp account page with the Extras dropdown opened](https://res.cloudinary.com/dza7lstvk/image/upload/v1750682741/Medusa%20Resources/CleanShot_2025-06-23_at_11.48.12_2x_l86ein.png) + +4. Scroll down to the "Your API keys" section and click on the "Create A Key" button. + +![Mailchimp API keys page with the Create A Key button highlighted](https://res.cloudinary.com/dza7lstvk/image/upload/v1750682771/Medusa%20Resources/CleanShot_2025-06-23_at_11.48.53_2x_vf3ftl.png) + +5. In the API key form, enter a name for the API key. +6. Click the "Generate Key" button to create the API key. + +![Mailchimp API key creation form with a name field](https://res.cloudinary.com/dza7lstvk/image/upload/v1750682817/Medusa%20Resources/CleanShot_2025-06-23_at_11.49.12_2x_xwwmi6.png) + +Copy the generated API key and add it to the `.env` file in your Medusa application: + +```shell +MAILCHIMP_API_KEY=123... +``` + +#### Retrieve Mailchimp Server Prefix + +You can retrieve your Mailchimp server prefix from the URL of your Mailchimp dashboard. It should be in the format `https://.admin.mailchimp.com/`. So, the server prefix will be something like `us5`, for example. + +Then, add the server prefix to the `.env` file in your Medusa application: + +```shell +MAILCHIMP_SERVER=us5 +``` + +#### Create Mailchimp Audience List + +Next, you'll create a Mailchimp audience list to store your subscribers: + +1. On the Mailchimp dashboard, click on "Audience" in the left sidebar. +2. Click on the dropdown next to the "Contacts" header and choose "Manage Audiences". + +![Mailchimp audience management page with the Manage Audiences option highlighted](https://res.cloudinary.com/dza7lstvk/image/upload/v1750683058/Medusa%20Resources/CleanShot_2025-06-23_at_11.50.54_2x_ayyyuq.png) + +3. Click on the "Create Audience" button at the top right. + +![Mailchimp audience creation page with the Create Audience button highlighted](https://res.cloudinary.com/dza7lstvk/image/upload/v1750683095/Medusa%20Resources/CleanShot_2025-06-23_at_11.51.51_2x_d0nxvc.png) + +4. Enter the audience details, such as the audience name, default from email address, and default from name. These defaults are used in the created campaigns. + +![Mailchimp audience creation form with fields for audience name, default from email address, and default from name](https://res.cloudinary.com/dza7lstvk/image/upload/v1750683517/Medusa%20Resources/CleanShot_2025-06-23_at_11.54.25_2x_ihgiyt.png) + +5. Once you're done, click the "Save" button to create the audience. This will open the Audience's contacts page. +6. Click on the "More options" button and select "Audience settings" from the dropdown. + +![Mailchimp audience contacts page with the More options button highlighted](https://res.cloudinary.com/dza7lstvk/image/upload/v1750683642/Medusa%20Resources/CleanShot_2025-06-23_at_16.00.19_2x_zjetg6.png) + +7. In the Audience settings page, copy the value of "Audience ID" from the first section. + +![Mailchimp audience settings page with the Audience ID highlighted](https://res.cloudinary.com/dza7lstvk/image/upload/v1750683763/Medusa%20Resources/CleanShot_2025-06-23_at_16.01.51_2x_a8grlw.png) + +Add the copied ID to the `.env` file in your Medusa application as the list ID: + +```shell +MAILCHIMP_LIST_ID=123... +``` + +#### Set Mailchimp Templates Options + +Finally, you can optionally set the Mailchimp templates options in the `.env` file. These options are used when sending newsletters about new products. + +```shell +MAILCHIMP_NEW_PRODUCTS_SUBJECT_LINE="Check out our new products!" +MAILCHIMP_NEW_PRODUCTS_STOREFRONT_URL=https://localhost:8000 +``` + +Where: + +- `MAILCHIMP_NEW_PRODUCTS_SUBJECT_LINE`: The subject line for the new products newsletter. +- `MAILCHIMP_NEW_PRODUCTS_STOREFRONT_URL`: The URL of your storefront where users can view the new products. In development, the Next.js Starter Storefront runs on `http://localhost:8000`, so you can set it to that URL. + +The Mailchimp integration is now ready. You'll test it out as you implement the subscription features in the next steps. + +*** + +## Optional Step: Send Welcome Emails from Mailchimp + +In the Mailchimp Notification Module Provider, you handle the `newsletter-signup` notification template by subscribing an email address to the Mailchimp audience list. However, you typically should also send a welcome email to the subscribed customer. + +To do that, you can create an automation flow in Mailchimp that automatically sends emails whenever a new subscriber is added to the audience list. + +To do that: + +1. On the Mailchimp dashboard, click on "Automations" in the sidebar. +2. Click on the "Build from scratch" button to create a new automation flow. + +![Mailchimp automations page with the Build from scratch button highlighted](https://res.cloudinary.com/dza7lstvk/image/upload/v1750684308/Medusa%20Resources/CleanShot_2025-06-23_at_12.18.15_2x_ymj8ar.png) + +3. In the flow creation form, enter a name for the automation flow and choose the Audience you created earlier. +4. Click the "Choose a starting point" button to select a template for the automation flow. + +![Mailchimp automation flow creation form with fields for flow name and audience selection](https://res.cloudinary.com/dza7lstvk/image/upload/v1750684445/Medusa%20Resources/CleanShot_2025-06-23_at_12.19.17_2x_yso5wu.png) + +5. A pop-up will open in the flow editor to choose a template to start from. Choose the "Signs up for Email" template. + +![Mailchimp automation flow editor with the Signs up for Email template highlighted](https://res.cloudinary.com/dza7lstvk/image/upload/v1750685997/Medusa%20Resources/CleanShot_2025-06-23_at_12.19.32_2x_x14tcv.png) + +6. In the flow editor, drag the "Send Email" action to the flow canvas. This will open a pop-up to configure the email. +7. In the email configuration pop-up, you can enter the email subject, from name, and from email address. + +![Mailchimp automation flow editor with the Send Email action highlighted](https://res.cloudinary.com/dza7lstvk/image/upload/v1750686062/Medusa%20Resources/CleanShot_2025-06-23_at_12.20.56_2x_iksdox.png) + +8. To set the email content, click the "Select a template" link in the email configuration pop-up. You can then choose an existing template or paste your own HTML content. + +### Example HTML Content + +```html + + + + + Thanks for signing up! + + + + + + +``` + +9. Once you're done, close the email configuration pop-up. The changes will be saved automatically. +10. In the flow editor, click the "Continue" button at the top right. + +![Mailchimp automation flow editor with the Continue button highlighted](https://res.cloudinary.com/dza7lstvk/image/upload/v1750686278/Medusa%20Resources/CleanShot_2025-06-23_at_12.24.15_2x_v1mcas.png) + +11. In the review pop-up, click the "Turn flow on" button to activate the automation flow. + +![Mailchimp automation flow review pop-up with the Turn flow on button highlighted](https://res.cloudinary.com/dza7lstvk/image/upload/v1750686313/Medusa%20Resources/CleanShot_2025-06-23_at_12.24.36_2x_wji8xx.png) + +Whenever a customer subscribes to the newsletter, Mailchimp will automatically send them a welcome email using the automation flow you created. + +*** + +## Step 3: Create Newsletter Subscription API Route + +Now that you've integrated Mailchimp with Medusa, you need to allow customers to subscribe to the newsletter. + +In this step, you will: + +- Create an [API route](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md) to subscribe customers. An API Route is an endpoint that exposes commerce features to external applications and clients, such as storefronts. + - In the API route, you'll emit an event indicating that the customer is signing up for the newsletter. +- Create a [subscriber](https://docs.medusajs.com/docs/learn/fundamentals/events-and-subscribers/index.html.md) that listens to the event emitted by the API route. The subscriber will use Mailchimp to subscribe the customer to the newsletter audience list. + +### a. Create the API Route + +An API route is created in a `route.ts` file under a sub-directory of the `src/api` directory. The path of the API route is the file's path relative to `src/api`. + +So, to create an API route at the path `/store/newsletter`, create the file `src/api/store/newsletter/route.ts` with the following content: + +```ts title="src/api/store/newsletter/route.ts" highlights={apiRouteHighlights} +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" +import { z } from "zod" + +export const newsletterSignupSchema = z.object({ + email: z.string().email(), + first_name: z.string().optional(), + last_name: z.string().optional(), +}) + +export async function POST( + req: MedusaRequest>, + res: MedusaResponse +) { + const eventModuleService = req.scope.resolve("event_bus") + + await eventModuleService.emit({ + name: "newsletter.signup", + data: { + email: req.validatedBody.email, + first_name: req.validatedBody.first_name, + last_name: req.validatedBody.last_name, + }, + }) + + res.json({ + success: true, + }) +} +``` + +You first export a [Zod](https://zod.dev/) schema object that you'll use to validate incoming request bodies. You expect the request body to have an `email` field, and optionally allow passing `first_name` and `last_name` fields. + +Then, you export a `POST` route handler function. This will expose a `POST` API route at `/store/newsletter`. The route handler function accepts two parameters: + +1. A request object with details and context on the request, such as body parameters. +2. A response object to manipulate and send the response. + +In the route handler, you use the [Medusa container](https://docs.medusajs.com/docs/learn/fundamentals/medusa-container/index.html.md) to resolve the [Event Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/event/index.html.md)'s service. + +Then, you emit the `newsletter.signup` event, passing as the event payload the email, first name, and last name from the request body. + +Finally, you send a JSON response indicating that the request was successful. + +### b. Add Validation Middleware + +To validate that requests sent to the `/store/newsletter` API route have the required body parameters, you'll add a validation [middleware](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/middlewares/index.html.md). A middleware is a function that is executed before an API route's handler when a request is made to the route. + +To apply the validation middleware on the `/store/newsletter` API route, create the file `src/api/middlewares.ts` with the following content: + +```ts title="src/api/middlewares.ts" +import { + defineMiddlewares, + validateAndTransformBody, +} from "@medusajs/framework/http" +import { newsletterSignupSchema } from "./store/newsletter/route" + +export default defineMiddlewares({ + routes: [ + { + matcher: "/store/newsletter", + methods: ["POST"], + middlewares: [ + validateAndTransformBody(newsletterSignupSchema), + ], + }, + ], +}) +``` + +You define middlewares using the `defineMiddlewares` function from the Medusa Framework. It accepts an object having a `routes` property, whose value is an array of middleware route objects. Each middleware route object has the following properties: + +- `matcher`: The path of the route the middleware applies to. +- `methods`: The HTTP methods the middleware applies to, which is in this case `POST`. +- `middlewares`: An array of middleware functions to apply to the route. You apply the `validateAndTransformBody` middleware which ensures that a request's body has the parameters required by a route. You pass it the schema you defined earlier in the API route's file. + +### c. Create Subscriber + +Finally, you'll create a subscriber that listens to the `newsletter.signup` event emitted by the API route. The subscriber will create a notification with the `newsletter-signup` template. Under the hoode, the Mailchimp Notification Module Provider will handle the notification and subscribe the customer to the newsletter audience list. + +Create the file `src/subscribers/newsletter-signup.ts` with the following content: + +```ts title="src/subscribers/newsletter-signup.ts" +import { SubscriberArgs, type SubscriberConfig } from "@medusajs/framework" + +export default async function orderPlacedHandler({ + event: { data }, + container, +}: SubscriberArgs<{ email: string, first_name: string, last_name: string }>) { + const notificationModuleService = container.resolve("notification") + + await notificationModuleService.createNotifications({ + channel: "email", + to: data.email, + template: "newsletter-signup", + data: { + first_name: data.first_name, + last_name: data.last_name, + }, + }) +} + +export const config: SubscriberConfig = { + event: `newsletter.signup`, +} +``` + +A subscriber file must export: + +1. An asynchronous function, which is the subscriber that is executed when the event is emitted. +2. A configuration object that holds the name of the event the subscriber listens to, which is `newsletter.signup` in this case. + +The subscriber function receives an object as a parameter that has a `container` property, which is the Medusa container. The Medusa container holds Framework and commerce tools that you can resolve and use in your customizations. + +In the subscriber function, you resolve the [Notification Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/notification/index.html.md)'s service from the Medusa container. Then, you call the `createNotifications` method to create a notification with the following properties: + +- `channel`: The channel to send the notification through, which is `email` in this case. Since the [Mailchimp Notification Module Provider is registered with the email channel](#g-add-module-provider-to-medusas-configurations), it will process the notification. +- `to`: The email address to subscribe to the newsletter. +- `template`: The template to use for the notification, which is `newsletter-signup`. +- `data`: An object containing additional data to pass to the notification, such as the user's first and last names. + +Now you have an API route that allows customers to subscribe to the newsletter. You'll test it when you customize the storefront in the next step. + +*** + +## Step 4: Add Newsletter Subscription Form in the Storefront + +In this step, you'll customize the [Next.js Starter Storefront](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/index.html.md) that you installed as part of the first step. You'll add a newsletter subscription form in the storefront's footer that allows customers to subscribe to the newsletter. + +The Next.js Starter Storefront was installed in a separate directory from Medusa. The directory's name is `{your-project}-storefront`. + +So, if your Medusa application's directory is `medusa-newsletter`, you can find the storefront by going back to the parent directory and changing to the `medusa-newsletter-storefront` directory: + +```bash +cd ../medusa-newsletter-storefront # change based on your project name +``` + +### a. Add Subscribe to Newsletter Function + +You'll start by adding a server function that sends a request to the `/store/newsletter` API route you created in the previous step to subscribe a customer to the newsletter. + +Create the file `src/lib/data/newsletter.ts` with the following content: + +```ts title="src/lib/data/newsletter.ts" badgeLabel="Storefront" badgeColor="blue" +"use server" + +import { sdk } from "@lib/config" + +export const subscribeToNewsletter = async (email: string) => { + const response = await sdk.client.fetch(`/store/newsletter`, { + method: "POST", + body: { + email, + }, + }) + + return response +} +``` + +You use the [JS SDK](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/js-sdk/index.html.md), which is already configured in the Next.js Starter Storefront, to send a `POST` request to the `/store/newsletter` API route. You pass the email address in the request body. + +### b. Create Newsletter Subscription Form + +Next, you'll create a newsletter subscription form component that allows customers to enter their email address and subscribe to the newsletter. + +Create the file `src/modules/layout/components/newsletter-form/index.tsx` with the following content: + +```tsx title="src/modules/layout/components/newsletter-form/index.tsx" badgeLabel="Storefront" badgeColor="blue" +"use client" + +import { subscribeToNewsletter } from "@lib/data/newsletter" +import { SubmitButton } from "@modules/checkout/components/submit-button" +import Input from "@modules/common/components/input" +import { useState } from "react" +import { toast } from "@medusajs/ui" + +const NewsletterForm = () => { + const [email, setEmail] = useState("") + const [loading, setLoading] = useState(false) + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + setLoading(true) + try { + await subscribeToNewsletter(email) + } catch (error) { + console.error(error) + // ignore, don't show error to user + } + toast.success("Thanks for subscribing!") + setEmail("") + setLoading(false) + } + + return ( +
+
+

Subscribe to our newsletter

+

+ Receive updates on our latest products and exclusive offers. +

+
+
+
+
+ setEmail(e.target.value)} + disabled={loading} + /> +
+
+ + Subscribe + +
+
+
+
+ ) +} + +export default NewsletterForm +``` + +You create a `NewsletterForm` component that renders a form with an email input and a submit button. + +Once the customer enters their email and submits the form, you use the `subscribeToNewsletter` function you created to subscribe the customer to the newsletter. + +You also show a toast notification to the customer indicating that they successfully subscribed to the newsletter. + +The `toast` function is imported from [Medusa UI](https://docs.medusajs.com/ui/index.html.md), which requires adding `Toaster` component in the application's tree. So, import the `Toaster` component in the file `src/app/[countryCode]/(main)/layout.tsx`: + +```tsx title="src/app/[countryCode]/(main)/layout.tsx" badgeLabel="Storefront" badgeColor="blue" +import { Toaster } from "@medusajs/ui" +``` + +Then, in the `PageLayout` component's return statement, add the `Toaster` component after the `Footer` component: + +```tsx title="src/app/[countryCode]/(main)/layout.tsx" badgeLabel="Storefront" badgeColor="blue" +return ( + <> + {/* ... */} +