Overview
Icon for SMTP Senpai

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 clipboardmsp_…) from the MailSenpai customer area, SMTP Senpai page

Install

npm install medusa-plugin-smtp-senpai
# or
yarn add medusa-plugin-smtp-senpai

Configure

Register the provider in the Notification Module, in Copy to clipboardmedusa-config.ts:

import { 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 domain
reply_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):

import { 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 h
headers: { 'X-Order': String(order.display_id) }, // optional, X- headers only
reply_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:

options: {
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>`,
}),
},
}
await 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 clipboardexternal_id.
  • API errors (for example an unverified sender domain) are thrown as Copy to clipboardMedusaError with 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.

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.

You may also like

Browse all integrations

Build your own

Develop your own custom integration

Build your own integration with our API to speed up your processes. Make your integration available via npm for it to be shared in our Library with the broader Medusa community.

gift card interface

Ready to build your custom commerce setup?