Dosu LogoDosu Logo
Ask
Join our Discord
StrapiPublic
Strapi
DocumentsStrapi
email
email
Type
External
Status
Published
Created
Mar 5, 2026
Updated
Jul 29, 2026
Updated by
Dosu Bot
Source
View

Email#

The Email feature sends transactional messages through local SMTP or external providers like SendGrid. Setup guidance in this documentation covers provider configuration and extending delivery via controllers or hooks.

The Email feature enables Strapi applications to send emails from a server or an external provider.

Free feature Email > "send" permission for the user to send emails via the backend server Available by default Available in both Development & Production environment

Configuration#

Most configuration options for the Email feature are handled via your Strapi project's code. The admin panel provides a read-only view of the current configuration, connection status, and provider capabilities, and lets users send a test email.

:::info Provider vs. host

  • The email provider refers to the package that Strapi calls to send an email (e.g. official providers such as Sendgrid or community packages such as @strapi/provider-email-nodemailer). Providers implement the logic for sending mail when Strapi invokes them.
  • The provider host (or server) refers to the connection details (e.g. an SMTP hostname, port, or REST API endpoint) that the provider exposes. Some providers hide these details behind an API key, while others require you to supply host-related options in your configuration.

The Email feature only handles outbound delivery. Receiving or parsing incoming messages is outside the scope of the built-in plugin and must be implemented with your email provider's inbound webhooks or a custom integration.
:::

Admin panel settings#

Path to configure the feature: Settings > Email feature > Configuration

<ThemedImage
alt="Email configuration"
sources={{
light: '/img/assets/settings/settings-email.png',
dark: '/img/assets/settings/settings-email_DARK.png',
}}
/>

The Configuration interface is read-only for most fields. It displays the current provider configuration and lets users test connectivity.

The following information is shown in the Configuration panel:

  • Default sender email and, if the configured defaultFrom address includes a display name, Default sender name.
  • Default response email and, if the configured defaultReplyTo address includes a display name, Default reply-to name.
  • Email provider: the provider currently in use.
Info

If the active provider supports SMTP connection verification (for example, the Nodemailer provider), a Connection status field is also shown with a Test connection button. Clicking it verifies the SMTP connection without sending a message. The button displays a Connected or Error badge depending on the result.

A Provider capabilities card appears below the main configuration when the active provider exposes SMTP metadata. It shows the SMTP server address, encryption protocol (TLS, STARTTLS, or None), authentication type and user, pool status (Idle or Active, when connection pooling is enabled), and badges for enabled features such as DKIM, OAuth2, rate limiting, and connection pool.

The only user-editable field on this page is the Recipient email field under Test email delivery. A Send test email button sends a test message to that address.

This page is only visible if the current role has the "Access the Email Settings page" permission enabled (see RBAC feature documentation for more information):

<ThemedImage
alt="Email configuration"
sources={{
light: '/img/assets/settings/settings-email-config-role.png',
dark: '/img/assets/settings/settings-email-config-role_DARK.png',
}}
/>

Code-based configuration#

The Email feature requires a provider and a provider configuration in the config/plugins.js|ts file. See providers for detailed installation and configuration instructions.

Sendmail is the default email provider in the Strapi Email feature. It uses internally for mail delivery. It provides functionality for the local development environment but is not production-ready in the default configuration. For production stage applications you need to further configure Sendmail or change providers.

Tip

For most production setups that use a dedicated SMTP relay, consider switching to @strapi/provider-email-nodemailer (set provider to "nodemailer" in your email plugin config). See the dedicated Nodemailer configuration documentation for details.

In non-production environments, Strapi logs a one-time warning when the sendmail provider is active, suggesting this switch.

Email configuration options#

Plugins configuration are defined in the config/plugins.js file or config/plugins.ts file. Please refer to providers for detailed provider-specific installation and configuration instructions.

OptionTypeDescriptionDefault ValueNotes
providerstringThe email provider to use.sendmailRequired
providerOptionsobjectThe email provider options.{}Optional
providerOptions.apiKeystringThe API key for the email provider.''Optional
settingsobjectThe email settings.{}Optional
settings.defaultFromstringThe default email address to use as the sender.''Optional
settings.defaultReplyTostringThe default email address to use as the reply-to address.''Optional
ratelimitobjectThe email rate limit settings.{}Optional
ratelimit.enabledbooleanWhether to enable rate limiting.trueOptional
ratelimit.intervalstringThe interval for rate limiting in minutes.5Optional
ratelimit.maxnumberThe maximum number of requests allowed during the interval.5Optional
ratelimit.delayAfternumberThe number of requests allowed before rate limiting is applied.1Optional
ratelimit.timeWaitnumberTime to wait before responding to a request (in milliseconds).1Optional
ratelimit.prefixKeystringThe prefix for the rate limit key.${userEmail}Optional
ratelimit.whitelistarray(string)Array of IP addresses to whitelist from rate limiting.[]Optional
ratelimit.storeobjectRate limiting storage location and for more information please see the .MemoryStoreOptional

Providers#

The Email feature can be extended via the installation and configuration of additional providers.

Providers add an extension to the core capabilities of the plugin, for example to use Amazon SES for emails instead of Sendmail.

There are both official providers maintained by Strapi — discoverable via the Marketplace — and many community maintained providers available via .

Installing providers#

New providers can be installed using npm or yarn using the following format @strapi/provider-<plugin>-<provider> --save.

For example, to install the Sendgrid provider:

yarn add @strapi/provider-email-sendgrid
npm install @strapi/provider-email-sendgrid --save
Configuring providers#

Newly installed providers are enabled and configured in the /config/plugins file. If this file does not exist you must create it.

:::info Specific email providers configurations

  • Each provider will have different configuration settings available. Review the respective entry for that provider in the Marketplace or to learn more.

  • For production scenarios with the Nodemailer provider (OAuth2, connection pooling, DKIM signing, rate limiting), see the dedicated documentation.
    :::

The following is an example configuration for the Sendgrid provider:

module.exports = ({ env }) => ({
  // ...
  email: {
    config: {
      provider: 'sendgrid', // For community providers pass the full package name (e.g. provider: 'strapi-provider-email-mandrill')
      providerOptions: {
        apiKey: env('SENDGRID_API_KEY'),
        // region: 'eu', // Optional: set to 'eu' for EU data residency (default: 'global')
      },
      settings: {
        defaultFrom: 'juliasedefdjian@strapi.io',
        defaultReplyTo: 'juliasedefdjian@strapi.io',
        testAddress: 'juliasedefdjian@strapi.io',
      },
    },
  },
  // ...
});
export default ({ env }) => ({
  // ...
  email: {
    config: {
      provider: 'sendgrid', // For community providers pass the full package name (e.g. provider: 'strapi-provider-email-mandrill')
      providerOptions: {
        apiKey: env('SENDGRID_API_KEY'),
        // region: 'eu', // Optional: set to 'eu' for EU data residency (default: 'global')
      },
      settings: {
        defaultFrom: 'juliasedefdjian@strapi.io',
        defaultReplyTo: 'juliasedefdjian@strapi.io',
        testAddress: 'juliasedefdjian@strapi.io',
      },
    },
  },
  // ...
});

:::tip EU data residency
If you use a SendGrid API key created on the , set region: 'eu' in providerOptions. EU API keys only work against the EU endpoint (https://api.eu.sendgrid.com); without this option, email delivery will fail with an Unauthorized error.
:::

:::note Notes

  • When using a different provider per environment, specify the correct configuration in /config/env/${yourEnvironment}/plugins.js|ts (see Environments).
  • Only one email provider will be active at a time. If the email provider setting isn't picked up by Strapi, verify the plugins.js|ts file is in the correct folder.
  • When testing the new email provider with those two email templates created during strapi setup, the shipper email on the template defaults to no-reply@strapi.io and needs to be updated according to your email provider, otherwise it will fail the test (see Configure templates locally).
  • For best deliverability, configure SPF/DKIM with your email provider and ensure the defaultFrom domain aligns with the domain you verified with the provider.
    :::
Per-environment configuration

When configuring your provider you might want to change the configuration based on the NODE_ENV environment variable or use environment specific credentials.

You can set a specific configuration in the /config/env/{env}/plugins.js|ts configuration file and it will be used to overwrite the default configuration.

Some providers expose SMTP-style connection details instead of (or in addition to) an API key. Add those values in providerOptions so Strapi can reach the provider host. For instance, the community Nodemailer provider expects the host, port, and authentication credentials:

module.exports = ({ env }) => ({
  email: {
    config: {
      provider: 'nodemailer',
      providerOptions: {
        host: env('SMTP_HOST'),
        port: env.int('SMTP_PORT', 587),
        secure: false, // Use `true` for port 465
        auth: {
          user: env('SMTP_USERNAME'),
          pass: env('SMTP_PASSWORD'),
        },
      },
      settings: {
        defaultFrom: 'no-reply@example.com',
        defaultReplyTo: 'support@example.com',
      },
    },
  },
});
export default ({ env }) => ({
  email: {
    config: {
      provider: 'nodemailer',
      providerOptions: {
        host: env('SMTP_HOST'),
        port: 587,
        secure: false, // Use `true` for port 465
        auth: {
          user: env('SMTP_USERNAME'),
          pass: env('SMTP_PASSWORD'),
        },
      },
      settings: {
        defaultFrom: 'no-reply@example.com',
        defaultReplyTo: 'support@example.com',
      },
    },
  },
});

If your provider gives you a single URL instead of host and port values, pass that URL (for example https://api.eu.mailgun.net) in providerOptions using the key the package expects.

Building a custom provider#

To build your own provider, publish it to npm, or use it locally in your project, see the dedicated documentation:

Usage#

The Email feature uses the Strapi global API, meaning it can be called from anywhere inside a Strapi application, either from the back-end server itself through a controller or service, or from the admin panel, for example in response to an event (using lifecycle hooks).

Sending emails with a controller or service {#controller-service}#

The Email feature has an email service that contains 2 functions to send emails:

  • send() directly contains the email contents,
  • sendTemplatedEmail() consumes data from the Content Manager to populate emails, streamlining programmatic emails.

Using the send() function#

To trigger an email in response to a user action add the send() function to a controller or service. The send function has the following properties:

PropertyTypeDescription
fromstring (email address)Sender address. If not specified, uses defaultFrom from plugins.js.
tostring (email address)Recipient address. Required.
ccstring (email address)Carbon copy recipients. Optional.
bccstring (email address)Blind carbon copy recipients. Optional.
replyTostring (email address)Reply-to address. If not specified, uses defaultReplyTo from plugins.js.
subjectstringEmail subject. Required.
textstringPlain-text body. Either text or html is required.
htmlstringHTML body. Either text or html is required.
attachmentsobject[]Array of attachment objects.
headersobjectCustom SMTP headers, for example { 'X-Custom-Header': 'value' }.
priority'high' | 'normal' | 'low'Email priority flag.
inReplyTostringMessage-ID of the email being replied to. Used for conversation threading.
referencesstring | string[]Message-ID list this email references. Used for conversation threading.
envelopeobjectCustom SMTP envelope with from and to fields. Useful for bounce handling.
listobjectRFC 2369 List-* headers. Enables one-click unsubscribe in Gmail and Outlook for newsletters.
icalEventobjectCalendar event invitation in iCalendar format. Attach with { method, content }.
dsnobjectDelivery Status Notification settings. Requests bounce or delivery confirmation reports.

:::note When using the Nodemailer provider
The Nodemailer provider uses an explicit allowlist for all send() fields. Unknown properties are silently dropped. For the complete list of supported fields — including dkim, amp, raw, auth (per-message OAuth2), and others — see the .
:::

The following code example can be used in a controller or a service:

await strapi.plugins['email'].services.email.send({
  to: 'valid email address',
  from: 'your verified email address', //e.g. single sender verification in SendGrid
  cc: 'valid email address',
  bcc: 'valid email address',
  replyTo: 'valid email address',
  subject: 'The Strapi Email feature worked successfully',
  text: 'Hello world!',
  html: 'Hello world!',
}),

Using the sendTemplatedEmail() function#

The sendTemplatedEmail() function is used to compose emails from a template. The function compiles the email from the available properties and then sends the email.

To use the sendTemplatedEmail() function, define the emailTemplate object and add the function to a controller or service. The function calls the emailTemplate object, and can optionally call the emailOptions and data objects:

ParameterDescriptionTypeDefault
emailOptions
Optional
Contains email addressing properties: to, from, replyTo, cc, and bccobject{ }
emailTemplateContains email content properties: subject, text, and html using object{ }
data
Optional
Contains the data used to compile the templatesobject{ }

The following code example can be used in a controller or a service:

const emailTemplate = {
  subject: 'Welcome <%= user.firstname %>',
  text: `Welcome to mywebsite.fr!
    Your account is now linked with: <%= user.email %>.`,
  html: `<h1>Welcome to mywebsite.fr!</h1>
    <p>Your account is now linked with: <%= user.email %>.<p>`,
};

await strapi.plugins['email'].services.email.sendTemplatedEmail(
  {
    to: user.email,
    // from: is not specified, the defaultFrom is used.
  },
    emailTemplate,
  {
    user: _.pick(user, ['username', 'email', 'firstname', 'lastname']),
  }
);

Sending emails from a lifecycle hook {#lifecycle-hook}#

To trigger an email based on administrator actions in the admin panel use lifecycle hooks and the send() function.

The following example illustrates how to send an email each time a new content entry is added in the Content Manager use the afterCreate lifecycle hook:


module.exports = {
    async afterCreate(event) { // Connected to "Save" button in admin panel
        const { result } = event;

        try{
            await strapi.plugin('email').service('email').send({ // you could also do: await strapi.service('plugin:email.email').send({
              to: 'valid email address',
              from: 'your verified email address', // e.g. single sender verification in SendGrid
              cc: 'valid email address',
              bcc: 'valid email address',
              replyTo: 'valid email address',
              subject: 'The Strapi Email feature worked successfully',
              text: '${fieldName}', // Replace with a valid field ID
              html: 'Hello world!', 

            })
        } catch(err) {
            console.log(err);
        }
    }
}

export default {
  async afterCreate(event) { // Connected to "Save" button in admin panel
    const { result } = event;

    try{
      await strapi.plugins['email'].services.email.send({
        to: 'valid email address',
        from: 'your verified email address', // e.g. single sender verification in SendGrid
        cc: 'valid email address',
        bcc: 'valid email address',
        replyTo: 'valid email address',
        subject: 'The Strapi Email feature worked successfully',
        text: '${fieldName}', // Replace with a valid field ID
        html: 'Hello world!', 
      })
    } catch(err) {
      console.log(err);
    }
  }
}

Documents
Admin Modal Management
Admin Panel Locale Management
admin-panel
faq
setting-up-admin-panel
Admin User Management
Blocks Editor
Clone Sanitizer Field Permissions
CodeMirror Integration
Content API Input Validation
breaking-changes
populate-select
rest
status
Content Manager Homepage
admin-panel
faq
quick-start
rest
Content Manager i18n Preview
Content Manager Layout Synchronization
Content Manager Preview
Content Manager URL & Filter State
Content Type Builder
admin-panel-api
controllers
create-components-for-plugins
populate-creator-fields
quick-start
store-and-access-data
Custom Field Prop Spreading
Date Field Serialization
Discard-Drafts Migration
Document ID Migration
breaking-changes
Draft & Publish Relation Synchronization
do-not-update-repeatable-components-with-document-service-api
document-service
lifecycle-hooks-document-service
populate
publishedat-always-set-when-dandp-disabled
relations
rest
DynamicZone Stability
components-and-dynamic-zones-do-not-return-id
components-dynamic-zones
no-shared-population-strategy-components-dynamic-zones
Edit View Layout Configuration
admin-panel
admin-panel-api
edit-view-layout-and-list-view-layout-rewritten
EnumerationInput Component
GraphQL Context Propagation
breaking-changes
document-service
draft-and-publish
graphql
i18n Locale Validation
breaking-changes
locale
i18n Locale-Scoped Operations
database-columns
document
document-service
draft-and-publish
i18n-content-manager-locale
locale
locale
locale
no-locale-all
parameters
populate
relations
rest
Monorepo Module Resolution
create-a-plugin
Nested Relation Modal Navigation
Polymorphic Relations
faq
Relation Field Validation
Relation Modal State Management
relations
Sharp Image Processing
media-library
Strapi Package Version Management
Strapi Project Scaffolding
create-a-plugin
How can I create my first Strapi project on a brand new MacBook with no prior coding experience, including all necessary terminal commands and setup steps?
quick-start
Strapi v5 Plugin API
admin-panel-rbac-store-updated
admin-permissions-for-plugins
breaking-changes
controllers
extension
faq
get-where-removed
helper-plugin
helper-plugin-deprecated
inject-content-manager-component
introduction
introduction-and-faq
model-config-path-uses-uid
pass-data-from-server-to-admin
plugins-migration
rbac
redux-content-manager-app-state
step-by-step
strapi-imports
Users-Permissions Plugin i18n
users-permissions
Vite Build Configuration
admin-panel
introduction-and-faq
vite
webpack-aliases-removed
Zod Schema Validation
document-service
Content Manager Mobile and Tablet UI Improvements
FilesManager API Reference
Focal Point Picker in Strapi Media Library
Nested Route File Structure for Strapi Plugins
Plugin-Specific Handling in the Strapi JavaScript Client
Strapi SDK Initialization and Architecture Guide
access-cast-environment-variables
access-configuration-values
adding-support-to-existing-project
admin-panel-customization
advanced-policies
advanced-queries
amazon-s3
api
api-tokens
attributes-and-content-types-names-reserved
audit-logs
auth-zero
authentication
aws-cognito
backend-customization
bulk-operations
bundlers
cas
cli
cli
client
cloudinary
community
configurations
configure-sso
content-api
content-history
content-manager
content-manager-apis
content-type-builder
core-service-methods-use-document-service
cron
crud
custom-fields
customization
data-management
database
database-identifiers-shortened
database-migrations
database-transactions
default-index-removed
default-input-validation
deployment
design-system
developing-plugins
development
discord
discord
docker
documentation
documents-and-entries
email
email-custom-providers
email-nodemailer
entity-service
entity-service-deprecated
environment
error-handling
examples
facebook
favicon
features
fetch
fields
filter
filtering
filters
filters
from-entity-service-to-document-service
functions
github
github
google
google
graphql
graphql-api-updated
guides
homepage
host-port-path
instagram
installation
installing-plugins-via-marketplace
interactive-query-builder
internationalization
intro
intro
is-supported-image-removed
keycloak
keycloak
koa-body-v6
license-only
linkedin
local-upload
locales-translations
logos
mailgun-provider-variables
media-library-providers
microsoft
middlewares
middlewares
middlewares
middlewares
models
mysql5-unsupported
new-provider-guide
new-response-format
no-find-page-in-document-service
no-upload-at-entry-creation
okta
only-better-sqlite3-for-sqlite
only-mysql2-package-for-mysql
openapi
order-pagination
order-pagination
patreon
plugin-sdk
plugin-structure
plugins
plugins-extension
policies
policies
populate
populating
preview
project-structure
publication-state-removed
query-engine
rbac
react-router-dom-6
reddit
register-allowed-fields
releases
remove-webhook-populate-relations
removed-support-for-some-env-options
requests-responses
review-workflows
routes
routes
sentry
server
server-api
server-default-log-level
server-proxy
services
services-and-controllers
setup-deployment
single-operations
sort-by-id
sort-pagination
sort-pagination
sso
status
strapi-container
strapi-utils-refactored
strict-requirements-config-files
templates
templates
testing
theme-extension
twitch
twitter
typescript
typescript
understanding-populate
upgrade-to-apollov4
upgrade-tool
upgrades
upload
usage-information
use-document-id
users-and-permissions-providers
vk
webhooks
wysiwyg-editor
yarn-not-default