SMTP Senpai
Send emails via the MailSenpai EU relay
medusa-plugin-smtp-senpai
Medusa v2 Notification Module Provider for SMTP Senpai, the EU SMTP relay and email API by MailSenpai. It handles the Copy to clipboardemail 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+.
Prerequisites
- An SMTP Senpai account with a verified sending domain
- An API key (Copy to clipboard
msp_…) from the MailSenpai customer area, SMTP Senpai page
Install
123npm install medusa-plugin-smtp-senpai# oryarn add medusa-plugin-smtp-senpai
Configure
Register the provider in the Notification Module, in Copy to clipboardmedusa-config.ts:
1234567891011121314151617181920import { defineConfig, Modules } from '@medusajs/framework/utils'module.exports = defineConfig({// ...modules: [{resolve: '@medusajs/medusa/notification',options: {providers: [{resolve: 'medusa-plugin-smtp-senpai',id: 'smtp-senpai',options: {channels: ['email'],api_key: process.env.SMTP_SENPAI_API_KEY,from: 'Your shop <orders@yourdomain.com>', // verified domainreply_to: 'support@yourdomain.com', // optional// templates: { ... } // optional, see below},},
Medusa allows one provider per channel: remove any other provider registered for Copy to clipboardemail.
option required description Copy to clipboardchannels yes Copy to clipboard['email'] Copy to clipboardapi_key yes API key (default: Copy to clipboardSMTP_SENPAI_API_KEY) Copy to clipboardfrom yes default sender, Copy to clipboardaddress or Copy to clipboardName <address> Copy to clipboardfrom_name no sender name when Copy to clipboardfrom has none Copy to clipboardreply_to no default Reply-To Copy to clipboardtemplates no templates by name, used when a notification has no Copy to clipboardcontent Copy to clipboardbase_url, Copy to clipboardtimeout_ms, Copy to clipboardmax_retries no client settings (retries only on 5xx and network errors)
Send a notification
With ready-made content (for example rendered with React Email in your subscriber):
1234567891011121314151617181920import { Modules } from '@medusajs/framework/utils'const notificationModuleService = container.resolve(Modules.NOTIFICATION)await notificationModuleService.createNotifications({to: order.email,channel: 'email',template: 'order-placed',content: {subject: `Order #${order.display_id} confirmed`,html: '<p>Thanks for your order.</p>',text: 'Thanks for your order.',},attachments: [{ filename: 'invoice.pdf', content: pdfBase64, content_type: 'application/pdf' },],provider_data: {idempotency_key: `order-placed-${order.id}`, // optional: never sent twice within 24 hheaders: { 'X-Order': String(order.display_id) }, // optional, X- headers onlyreply_to: 'orders@yourdomain.com', // optional
Templates in the provider options
When a notification has no Copy to clipboardcontent, the provider looks up Copy to clipboardtemplates[notification.template]. A template is either strings with Copy to clipboard{{ path }} placeholders filled from Copy to clipboardnotification.data (values are HTML-escaped in Copy to clipboardhtml), or a function:
12345678910111213141516options: {channels: ['email'],api_key: process.env.SMTP_SENPAI_API_KEY,from: 'orders@yourdomain.com',templates: {'order-placed': {subject: 'Order #{{ order.display_id }} confirmed',html: '<p>Hi {{ customer.first_name }}, thanks for your order.</p>',text: 'Hi {{ customer.first_name }}, thanks for your order.',},'password-reset': (data) => ({subject: 'Reset your password',html: `<a href="${data.url}">Reset password</a>`,}),},}
123456await notificationModuleService.createNotifications({to: customer.email,channel: 'email',template: 'order-placed',data: { order: { display_id: order.display_id }, customer: { first_name: customer.first_name } },})
Result and errors
- On success the notification gets the SMTP Senpai Message-ID as Copy to clipboard
external_id. - API errors (for example an unverified sender domain) are thrown as Copy to clipboard
MedusaErrorwith the API message, so the notification is marked as failed. - A recipient on the SMTP Senpai suppression list is not emailed and the notification fails with the reason.
Links
- SMTP Senpai: https://en.mailsenpai.com/smtp-senpai/
- API documentation: https://en.mailsenpai.com/smtp-api/
- All MailSenpai integrations: https://en.mailsenpai.com/integrations/
- Source code and issues: https://gitlab.com/smtp-senpai/mailsenpai-js/-/tree/main/medusa-plugin-smtp-senpai
- Support: https://en.mailsenpai.com/contact-us/
License
MIT © MailSenpai
First received message and troubleshooting
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.
