Medusa v2 notification module provider for SMTP Senpai by MailSenpai: send order and customer emails on the email channel through the EU SMTP relay API.
Medusa v2 Notification Module Provider for SMTP Senpai, the EU SMTP relay and email API by MailSenpai. It handles the channel: order confirmations, password resets, invites and any notification you create with the Notification Module go out through the SMTP Senpai REST API.
Requires Medusa 2.x and Node.js 20+.
Register the provider in the Notification Module, in :
Medusa allows one provider per channel: remove any other provider registered for .
| option | required | description |
|---|---|---|
| yes | ||
| yes | API key (default: ) | |
| yes | default sender, or | |
| no | sender name when has none | |
| no | default Reply-To | |
| no | templates by name, used when a notification has no | |
| , , | no | client settings (retries only on 5xx and network errors) |
With ready-made content (for example rendered with React Email in your subscriber):
When a notification has no , the provider looks up . A template is either strings with placeholders filled from (values are HTML-escaped in ), or a function:
MIT © MailSenpai
Use a demo order on staging and trigger its notification subscriber. Confirm that the email channel selects this provider and that its external_id is recorded. Installing the provider does not create an order subscriber or a template for your store.
A relay/API acceptance result is distinct from inbox placement. Check the actual received message and its authentication headers. Keep API keys and customer data out of public screenshots and logs.
If authentication fails, check the key and account state. For a sender-domain rejection, verify the sender domain. For quota/rate errors, respect the response and the plan limit rather than retrying blindly. For a timeout, check outbound HTTPS access. Use a stable idempotency key for retries where the integration exposes it.
Package catalogue · API errors and delivery events · Integration guides
Service logs are retained for 7 days on the free plan and 90 days on paid plans. Local application logs may have different retention.
1npm install medusa-plugin-smtp-senpai2# or3yarn add medusa-plugin-smtp-senpai1import { defineConfig, Modules } from '@medusajs/framework/utils'2
3module.exports = defineConfig({4 // ...5 modules: [6 {7 resolve: '@medusajs/medusa/notification',8 options: {9 providers: [10 {11 resolve: 'medusa-plugin-smtp-senpai',12 id: 'smtp-senpai',13 options: {14 channels: ['email'],15 api_key: process.env.SMTP_SENPAI_API_KEY,16 from: 'Your shop <orders@yourdomain.com>', // verified domain17 reply_to: 'support@yourdomain.com', // optional18 // templates: { ... } // optional, see below19 },20 },21 ],22 },23 },24 ],25})1import { Modules } from '@medusajs/framework/utils'2
3const notificationModuleService = container.resolve(Modules.NOTIFICATION)4
5await notificationModuleService.createNotifications({6 to: order.email,7 channel: 'email',8 template: 'order-placed',9 content: {10 subject: `Order #${order.display_id} confirmed`,11 html: '<p>Thanks for your order.</p>',12 text: 'Thanks for your order.',13 },14 attachments: [15 { filename: 'invoice.pdf', content: pdfBase64, content_type: 'application/pdf' },16 ],17 provider_data: {18 idempotency_key: `order-placed-${order.id}`, // optional: never sent twice within 24 h19 headers: { 'X-Order': String(order.display_id) }, // optional, X- headers only20 reply_to: 'orders@yourdomain.com', // optional21 },22})1options: {2 channels: ['email'],3 api_key: process.env.SMTP_SENPAI_API_KEY,4 from: 'orders@yourdomain.com',5 templates: {6 'order-placed': {7 subject: 'Order #{{ order.display_id }} confirmed',8 html: '<p>Hi {{ customer.first_name }}, thanks for your order.</p>',9 text: 'Hi {{ customer.first_name }}, thanks for your order.',10 },11 'password-reset': (data) => ({12 subject: 'Reset your password',13 html: `<a href="${data.url}">Reset password</a>`,14 }),15 },16}1await notificationModuleService.createNotifications({2 to: customer.email,3 channel: 'email',4 template: 'order-placed',5 data: { order: { display_id: order.display_id }, customer: { first_name: customer.first_name } },6})