EmailGetting StartedConfiguration

Configuration

Configure email providers in your application

Last updated:

Configuration

This guide covers how to configure email providers in your application.

Provider Configuration

Each provider has its own configuration options. Here are examples for common providers:

Resend

import { resendProvider } from "@visulima/email/providers/resend";

const resend = resendProvider({
    apiKey: process.env.RESEND_API_KEY!,
    timeout: 30000, // Optional: request timeout in milliseconds
    retries: 3, // Optional: number of retry attempts
});

AWS SES

import { awsSesProvider } from "@visulima/email/providers/aws-ses";

const awsSes = awsSesProvider({
    accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
    region: "us-east-1", // Optional, defaults to us-east-1
    timeout: 30000, // Optional
    retries: 3, // Optional
});

SMTP

import { smtpProvider } from "@visulima/email/providers/smtp";

const smtp = smtpProvider({
    host: "smtp.example.com",
    port: 587,
    secure: false, // true for 465, false for other ports
    user: process.env.SMTP_USER!,
    password: process.env.SMTP_PASSWORD!,
    timeout: 30000, // Optional
    retries: 3, // Optional
});

Zeptomail

import { zeptomailProvider } from "@visulima/email/providers/zeptomail";

const zeptomail = zeptomailProvider({
    token: process.env.ZEPTOMAIL_TOKEN!, // Format: "Zoho-enczapikey <your_api_key>"
    timeout: 30000, // Optional
    retries: 3, // Optional
});

Brevo

import { brevoProvider } from "@visulima/email/providers/brevo";

const brevo = brevoProvider({
    apiKey: process.env.BREVO_API_KEY!,
    endpoint: "https://api.brevo.com/v3", // Optional, defaults to this
    timeout: 30000, // Optional
    retries: 3, // Optional
});

Mailjet

import { mailjetProvider } from "@visulima/email/providers/mailjet";

const mailjet = mailjetProvider({
    apiKey: process.env.MAILJET_API_KEY!,
    apiSecret: process.env.MAILJET_API_SECRET!,
    endpoint: "https://api.mailjet.com", // Optional, defaults to this
    timeout: 30000, // Optional
    retries: 3, // Optional
});

MailerSend

import { mailerSendProvider } from "@visulima/email/providers/mailersend";

const mailerSend = mailerSendProvider({
    apiToken: process.env.MAILERSEND_API_TOKEN!,
    endpoint: "https://api.mailersend.com", // Optional, defaults to this
    timeout: 30000, // Optional
    retries: 3, // Optional
});

Mandrill

import { mandrillProvider } from "@visulima/email/providers/mandrill";

const mandrill = mandrillProvider({
    apiKey: process.env.MANDRILL_API_KEY!,
    endpoint: "https://mandrillapp.com/api/1.0", // Optional, defaults to this
    timeout: 30000, // Optional
    retries: 3, // Optional
});

MailPace

import { mailPaceProvider } from "@visulima/email/providers/mailpace";

const mailPace = mailPaceProvider({
    apiToken: process.env.MAILPACE_API_TOKEN!,
    endpoint: "https://app.mailpace.com/api/v1", // Optional, defaults to this
    timeout: 30000, // Optional
    retries: 3, // Optional
});

Mailtrap

import { mailtrapProvider } from "@visulima/email/providers/mailtrap";

const mailtrap = mailtrapProvider({
    apiToken: process.env.MAILTRAP_API_TOKEN!,
    endpoint: "https://send.api.mailtrap.io", // Optional, defaults to this
    timeout: 30000, // Optional
    retries: 3, // Optional
});

Postal

import { postalProvider } from "@visulima/email/providers/postal";

const postal = postalProvider({
    host: process.env.POSTAL_HOST!,
    apiKey: process.env.POSTAL_API_KEY!,
    endpoint: `https://${process.env.POSTAL_HOST}/api/v1`, // Optional, auto-generated from host
    timeout: 30000, // Optional
    retries: 3, // Optional
});

Azure Communication Services

import { azureProvider } from "@visulima/email/providers/azure";

// Using access token
const azure = azureProvider({
    accessToken: process.env.AZURE_ACCESS_TOKEN!,
    region: process.env.AZURE_REGION!, // e.g., "eastus", "westus"
    endpoint: `https://${process.env.AZURE_REGION}.communication.azure.com`, // Optional, auto-generated
    timeout: 30000, // Optional
    retries: 3, // Optional
});

// Or using connection string
const azureWithConnectionString = azureProvider({
    connectionString: process.env.AZURE_CONNECTION_STRING!,
    region: process.env.AZURE_REGION!,
});

Infobip

import { infobipProvider } from "@visulima/email/providers/infobip";

const infobip = infobipProvider({
    apiKey: process.env.INFOBIP_API_KEY!,
    baseUrl: "https://api.infobip.com", // Optional, defaults to this
    timeout: 30000, // Optional
    retries: 3, // Optional
});

Scaleway

import { scalewayProvider } from "@visulima/email/providers/scaleway";

const scaleway = scalewayProvider({
    apiKey: process.env.SCALEWAY_API_KEY!,
    region: process.env.SCALEWAY_REGION!, // e.g., "fr-par", "nl-ams"
    endpoint: "https://api.scaleway.com/transactional-email/v1alpha1", // Optional, defaults to this
    timeout: 30000, // Optional
    retries: 3, // Optional
});

AhaSend

import { ahaSendProvider } from "@visulima/email/providers/ahasend";

const ahaSend = ahaSendProvider({
    apiKey: process.env.AHASEND_API_KEY!,
    endpoint: "https://api.ahasend.com", // Optional, defaults to this
    timeout: 30000, // Optional
    retries: 3, // Optional
});

Mailomat

import { mailomatProvider } from "@visulima/email/providers/mailomat";

const mailomat = mailomatProvider({
    apiKey: process.env.MAILOMAT_API_KEY!,
    endpoint: "https://api.mailomat.com", // Optional, defaults to this
    timeout: 30000, // Optional
    retries: 3, // Optional
});

Sweego

import { sweegoProvider } from "@visulima/email/providers/sweego";

const sweego = sweegoProvider({
    apiKey: process.env.SWEEGO_API_KEY!,
    endpoint: "https://api.sweego.com", // Optional, defaults to this
    timeout: 30000, // Optional
    retries: 3, // Optional
});

Mailgun

import { mailgunProvider } from "@visulima/email/providers/mailgun";

const mailgun = mailgunProvider({
    apiKey: process.env.MAILGUN_API_KEY!,
    domain: process.env.MAILGUN_DOMAIN!, // Required: Your Mailgun domain
    endpoint: "https://api.mailgun.net", // Optional: Use https://api.eu.mailgun.net for EU accounts
    timeout: 30000, // Optional
    retries: 3, // Optional
});

Postmark

import { postmarkProvider } from "@visulima/email/providers/postmark";

const postmark = postmarkProvider({
    serverToken: process.env.POSTMARK_SERVER_TOKEN!,
    endpoint: "https://api.postmarkapp.com", // Optional, defaults to this
    timeout: 30000, // Optional
    retries: 3, // Optional
});

SendGrid

import { sendGridProvider } from "@visulima/email/providers/sendgrid";

const sendgrid = sendGridProvider({
    apiKey: process.env.SENDGRID_API_KEY!,
    endpoint: "https://api.sendgrid.com/v3", // Optional, defaults to this
    timeout: 30000, // Optional
    retries: 3, // Optional
});

Plunk

import { plunkProvider } from "@visulima/email/providers/plunk";

const plunk = plunkProvider({
    apiKey: process.env.PLUNK_API_KEY!,
    endpoint: "https://api.useplunk.com/v1", // Optional, defaults to this
    timeout: 30000, // Optional
    retries: 3, // Optional
});

Mock (for Testing)

import { mockProvider } from "@visulima/email/providers/mock";

const mock = mockProvider({
    debug: true, // Optional: enable debug logging
    delay: 100, // Optional: simulate delay in milliseconds
    failureRate: 0.1, // Optional: failure rate (0.0 to 1.0)
    simulateFailure: false, // Optional: always fail if true
    timeout: 0, // Optional: not used by mock provider
    retries: 0, // Optional: not used by mock provider
});

Environment Variables

It's recommended to store sensitive configuration in environment variables:

# .env
RESEND_API_KEY=re_xxx
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
SMTP_USER=user@example.com
SMTP_PASSWORD=password
ZEPTOMAIL_TOKEN=Zoho-enczapikey YOUR_API_KEY_HERE
BREVO_API_KEY=your-brevo-api-key
MAILJET_API_KEY=your-mailjet-api-key
MAILJET_API_SECRET=your-mailjet-api-secret
MAILERSEND_API_TOKEN=your-mailersend-api-token
MANDRILL_API_KEY=your-mandrill-api-key
MAILPACE_API_TOKEN=your-mailpace-api-token
MAILTRAP_API_TOKEN=your-mailtrap-api-token
POSTAL_HOST=your-postal-server.com
POSTAL_API_KEY=your-postal-api-key
AZURE_ACCESS_TOKEN=your-azure-access-token
AZURE_REGION=eastus
AZURE_CONNECTION_STRING=endpoint=https://...;accesskey=...
INFOBIP_API_KEY=your-infobip-api-key
SCALEWAY_API_KEY=your-scaleway-api-key
SCALEWAY_REGION=fr-par
AHASEND_API_KEY=your-ahasend-api-key
MAILOMAT_API_KEY=your-mailomat-api-key
SWEEGO_API_KEY=your-sweego-api-key
MAILGUN_API_KEY=your-mailgun-api-key
MAILGUN_DOMAIN=your-domain.com
POSTMARK_SERVER_TOKEN=your-postmark-server-token
SENDGRID_API_KEY=SG.your-sendgrid-api-key
PLUNK_API_KEY=your-plunk-api-key

Then use them in your code:

const resend = resendProvider({
    apiKey: process.env.RESEND_API_KEY!,
});

Typing Configuration

If you want to type your configuration objects, you can import the config types from each provider:

import { resendProvider, type ResendConfig } from "@visulima/email/providers/resend";
import { smtpProvider, type SmtpConfig } from "@visulima/email/providers/smtp";

// Type your configuration
const resendConfig: ResendConfig = {
    apiKey: process.env.RESEND_API_KEY!,
    timeout: 30000,
    retries: 3,
};

const smtpConfig: SmtpConfig = {
    host: "smtp.example.com",
    port: 587,
    secure: false,
    user: process.env.SMTP_USER!,
    password: process.env.SMTP_PASSWORD!,
};

// Use typed configs
const resend = resendProvider(resendConfig);
const smtp = smtpProvider(smtpConfig);

Each provider exports its own config type from its provider path. For example:

  • ResendConfig from @visulima/email/providers/resend
  • SmtpConfig from @visulima/email/providers/smtp
  • AwsSesConfig from @visulima/email/providers/aws-ses
  • BrevoConfig from @visulima/email/providers/brevo
  • MailgunConfig from @visulima/email/providers/mailgun
  • MailjetConfig from @visulima/email/providers/mailjet
  • MailerSendConfig from @visulima/email/providers/mailersend
  • MandrillConfig from @visulima/email/providers/mandrill
  • MailPaceConfig from @visulima/email/providers/mailpace
  • MailtrapConfig from @visulima/email/providers/mailtrap
  • PostalConfig from @visulima/email/providers/postal
  • PostmarkConfig from @visulima/email/providers/postmark
  • AzureConfig from @visulima/email/providers/azure
  • InfobipConfig from @visulima/email/providers/infobip
  • ScalewayConfig from @visulima/email/providers/scaleway
  • AhaSendConfig from @visulima/email/providers/ahasend
  • MailomatConfig from @visulima/email/providers/mailomat
  • SweegoConfig from @visulima/email/providers/sweego
  • SendGridConfig from @visulima/email/providers/sendgrid
  • PlunkConfig from @visulima/email/providers/plunk
  • MockConfig from @visulima/email/providers/mock
  • OpenTelemetryConfig from @visulima/email/providers/opentelemetry
  • And so on for other providers

Provider Factory Pattern

All providers follow a factory pattern. They return a ProviderFactory function that creates a provider instance:

import { resendProvider } from "@visulima/email/providers/resend";

// resendProvider is a factory function
const providerFactory = resendProvider;

// Call it with configuration to get a provider
const provider = providerFactory({ apiKey: "re_xxx" });

Multiple Providers

You can configure multiple providers and switch between them:

import { createMail } from "@visulima/email";
import { resendProvider } from "@visulima/email/providers/resend";
import { smtpProvider } from "@visulima/email/providers/smtp";

const resend = resendProvider({ apiKey: "re_xxx" });
const smtp = smtpProvider({
    host: "smtp.example.com",
    port: 587,
    user: "user@example.com",
    password: "password",
});

// Use Resend for production
const productionMail = createMail(resend);

// Use SMTP for development
const developmentMail = createMail(smtp);

Failover Configuration

Configure failover to automatically switch providers on failure:

import { failoverProvider } from "@visulima/email/providers/failover";
import { resendProvider } from "@visulima/email/providers/resend";
import { smtpProvider } from "@visulima/email/providers/smtp";

const failover = failoverProvider({
    mailers: [
        resendProvider({ apiKey: "re_xxx" }),
        smtpProvider({
            host: "smtp.example.com",
            port: 587,
            user: "user@example.com",
            password: "password",
        }),
    ],
    retryAfter: 60, // Wait 60 seconds before trying next provider
    timeout: 30000,
    retries: 3,
});

Round Robin Configuration

Configure round robin for load balancing:

import { roundRobinProvider } from "@visulima/email/providers/roundrobin";
import { resendProvider } from "@visulima/email/providers/resend";
import { smtpProvider } from "@visulima/email/providers/smtp";

const roundRobin = roundRobinProvider({
    mailers: [
        resendProvider({ apiKey: "re_xxx" }),
        smtpProvider({
            host: "smtp.example.com",
            port: 587,
            user: "user@example.com",
            password: "password",
        }),
    ],
    retryAfter: 60,
    timeout: 30000,
    retries: 3,
});

Default Email Configuration

You can configure default values for all emails sent through a Mail instance. This is useful when you want to set a default from address, replyTo address, or headers that apply to all emails.

Setting Default From Address

import { createMail } from "@visulima/email";
import { resendProvider } from "@visulima/email/providers/resend";

const mail = createMail(resendProvider({ apiKey: "re_xxx" }));

// Set default from address
mail.setFrom({ email: "noreply@example.com", name: "My App" });

// Now all emails will use this from address if not specified
const message = new MailMessage().to("user@example.com").subject("Hello").html("<h1>Hello World</h1>");
// No need to set .from() - it will use the default

await mail.send(message);

Setting Default Reply-To Address

const mail = createMail(resendProvider({ apiKey: "re_xxx" }));

// Set default reply-to address
mail.setReplyTo({ email: "support@example.com" });

// All emails will include this reply-to if not specified in the message

Setting Default Headers

const mail = createMail(resendProvider({ apiKey: "re_xxx" }));

// Set default headers
mail.setHeaders({
    "X-App-Name": "MyApp",
    "X-Version": "1.0.0",
    "X-Environment": "production",
});

// These headers will be added to all emails
// Message-specific headers will take precedence if they conflict

Chaining Configuration Methods

You can chain the configuration methods together:

const mail = createMail(resendProvider({ apiKey: "re_xxx" }))
    .setFrom({ email: "noreply@example.com", name: "My App" })
    .setReplyTo({ email: "support@example.com" })
    .setHeaders({ "X-App-Name": "MyApp" });

Overriding Defaults

Default values are only applied if the corresponding field is not set in the message:

const mail = createMail(resendProvider({ apiKey: "re_xxx" }));
mail.setFrom({ email: "noreply@example.com" });

// This message will use the default from address
const message1 = new MailMessage().to("user@example.com").subject("Hello").html("<h1>Hello</h1>");
await mail.send(message1); // Uses noreply@example.com

// This message overrides the default
const message2 = new MailMessage()
    .to("user@example.com")
    .from({ email: "custom@example.com" }) // Overrides default
    .subject("Hello")
    .html("<h1>Hello</h1>");
await mail.send(message2); // Uses custom@example.com

Header Merging

When setting default headers, they are merged with message-specific headers. Message headers take precedence:

const mail = createMail(resendProvider({ apiKey: "re_xxx" }));
mail.setHeaders({
    "X-App-Name": "MyApp",
    "X-Version": "1.0.0",
});

const message = new MailMessage()
    .to("user@example.com")
    .from("sender@example.com")
    .subject("Hello")
    .html("<h1>Hello</h1>")
    .header("X-Version", "2.0.0") // Overrides default
    .header("X-Custom", "value"); // Additional header

// Final headers will be:
// X-App-Name: MyApp (from default)
// X-Version: 2.0.0 (from message, overrides default)
// X-Custom: value (from message)

Next Steps

Support

Contribute to our work and keep us going

Community is the heart of open source. The success of our packages wouldn't be possible without the incredible contributions of users, testers, and developers who collaborate with us every day.Want to get involved? Here are some tips on how you can make a meaningful impact on our open source projects.

Ready to help us out?

Be sure to check out the package's contribution guidelines first. They'll walk you through the process on how to properly submit an issue or pull request to our repositories.

Submit a pull request

Found something to improve? Fork the repo, make your changes, and open a PR. We review every contribution and provide feedback to help you get merged.

Good first issues

Simple issues suited for people new to open source development, and often a good place to start working on a package.
View good first issues