Volanea

Volanea

By Volanea

Send Strapi's transactional email through Volanea — admin invites, password resets, user confirmation emails, and anything sent with the Email plugin.

Latest version

v1.0.0

released August 28, 2026

npm install strapi-provider-email-volanea

strapi-provider-email-volanea

Send Strapi's transactional email through Volanea — admin invites, password resets, user confirmation emails, and anything you send with the Email plugin.

Zero runtime dependencies. Works with Strapi v5 on Node 18+.

Install

npm install strapi-provider-email-volanea
# or
yarn add strapi-provider-email-volanea

Configure

config/plugins.js:

module.exports = ({ env }) => ({
  email: {
    config: {
      provider: 'strapi-provider-email-volanea',
      providerOptions: {
        apiKey: env('VOLANEA_API_KEY'),
      },
      settings: {
        defaultFrom: env('VOLANEA_DEFAULT_FROM', 'no-reply@yourdomain.com'),
        defaultFromName: env('VOLANEA_DEFAULT_FROM_NAME', 'Your App'),
        defaultReplyTo: env('VOLANEA_DEFAULT_REPLY_TO', 'support@yourdomain.com'),
      },
    },
  },
});

TypeScript projects — config/plugins.ts:

export default ({ env }) => ({
  email: {
    config: {
      provider: 'strapi-provider-email-volanea',
      providerOptions: {
        apiKey: env('VOLANEA_API_KEY'),
      },
      settings: {
        defaultFrom: env('VOLANEA_DEFAULT_FROM'),
        defaultFromName: env('VOLANEA_DEFAULT_FROM_NAME'),
        defaultReplyTo: env('VOLANEA_DEFAULT_REPLY_TO'),
      },
    },
  },
});

.env:

VOLANEA_API_KEY=sk_live_xxxxxxxxxxxx
VOLANEA_DEFAULT_FROM=no-reply@yourdomain.com
VOLANEA_DEFAULT_FROM_NAME=Your App
VOLANEA_DEFAULT_REPLY_TO=support@yourdomain.com

Your defaultFrom domain must be verified in Volanea before anything sends — an unverified domain returns HTTP 403. Verify it under Domains in the Volanea dashboard.

Send

await strapi.plugins.email.services.email.send({
  to: 'customer@example.com',
  subject: 'Welcome aboard',
  html: '<p>Thanks for signing up.</p>',
  text: 'Thanks for signing up.',
});

Volanea templates

Skip inline HTML and render a stored template instead:

await strapi.plugins.email.services.email.send({
  to: 'customer@example.com',
  templateId: 'tpl_welcome',
  variables: { firstName: 'Pankaj', plan: 'Growth' },
});

subject is optional when the template supplies one.

Scheduling

await strapi.plugins.email.services.email.send({
  to: 'customer@example.com',
  subject: 'Your trial ends tomorrow',
  html: '<p>…</p>',
  sendAt: '2026-09-01T09:00:00+05:30',
});

Attachments

await strapi.plugins.email.services.email.send({
  to: 'customer@example.com',
  subject: 'Your invoice',
  html: '<p>Invoice attached.</p>',
  attachments: [
    {
      filename: 'invoice.pdf',
      content: pdfBuffer, // Buffer, Uint8Array, string, or data: URI
      contentType: 'application/pdf',
    },
  ],
});

Up to 10 attachments per message, 10 MiB per request. The provider does not read attachments from disk or from a URL — pass the bytes.

A content string is treated as raw text, the same way Nodemailer treats it. If you already hold encoded bytes, say so rather than passing them as a plain string, or they will be encoded a second time:

{ filename: 'invoice.pdf', content: base64String, encoding: 'base64' }
// or
{ filename: 'invoice.pdf', content: 'data:application/pdf;base64,JVBERi0…' }

encoding accepts any Node Buffer encoding (base64, hex, utf8, …).

Options

providerOptions

OptionTypeDefaultDescription
apiKeystring—Required. Volanea secret key.
baseUrlstringhttps://api.volanea.comOverride the API host.
timeoutnumber15000Request timeout in ms.
throwOnSkipbooleantrueThrow when every recipient was skipped. See below.

settings

SettingDescription
defaultFromFrom address used when a send does not specify one.
defaultFromNameDisplay name for the sender.
defaultReplyToReply-To used when a send does not specify one.
defaultSubjectFallback subject line.
defaultTypetransactional (default) or marketing.

Behaviour worth knowing

A resolved send() does not mean delivered. Volanea queues the message and hands it to the provider after responding. Track final delivery with GET /v1/emails/{id} or the email.* webhooks, not from the return value.

Skipped recipients throw by default. Volanea returns HTTP 200 when a recipient is suppressed, unsubscribed, or over quota — the message just never sends. Strapi treats a resolved promise as success, so a suppressed address would silently swallow a password reset. This provider throws all_recipients_skipped when every recipient was skipped. Set throwOnSkip: false to opt out and read result.skipped yourself.

cc and bcc become extra recipients. Volanea sends one discrete message per recipient, so cc and bcc addresses each get their own copy and cannot see one another — bcc semantics, not cc semantics.

Marketing mail must say so. Inline HTML defaults to transactional, which skips the unsubscribe footer and the unsubscribe check. Pass type: 'marketing' for promotional content.

Errors

Failures throw a VolaneaEmailError with a machine-readable code. Branch on code, never on the message text.

codeMeaning
template_not_foundtemplateId does not exist.
send_failedPipeline failure with no more specific cause.
validation_errorBad request body; see error.details.
unauthorizedMissing or invalid API key.
rate_limitedPer-project send limit hit.
all_recipients_skipped200 response, nothing sent; see error.skipped.
timeoutNo response within timeout ms.
network_errorCould not reach the API.
invalid_attachmentAttachment had no content.

Troubleshooting

403 on every send — the from domain is not verified in Volanea. Check Domains in the dashboard; defaultFrom must sit on a verified domain.

Nothing arrives, no error — check result.skipped. A suppressed address stays suppressed until you remove it under Suppressions.

Emails send in development but not production — you are probably using a test-mode key. Test-mode sends render and log but never leave the building.

global fetch is unavailable — you are on Node 16 or older. Strapi v5 requires Node 18+.

Testing locally

Send to success@simulator.amazonses.com. It is absorbed by the SES mailbox simulator: it never bounces and never counts against your quota, bounce rate, or complaint rate. Never invent an address on a real domain to test with — it will hard-bounce and damage your sender reputation.

node test.js   # 21 checks, no network, no dependencies

License

MIT

Submit your content

Share your work with the community and get it listed in the Strapi ecosystem for everyone to discover and use.

Submit
Submit your content