PhoenixKit.Mailer (phoenix_kit v1.7.207)

Copy Markdown View Source

Mailer module for PhoenixKit emails.

This module handles sending emails such as confirmation emails, password reset emails, magic link emails, etc.

It can work in two modes:

  1. Built-in mode: Uses PhoenixKit's own Swoosh mailer (default)
  2. Delegation mode: Uses the parent application's mailer when configured

Configuration

To use your application's mailer instead of PhoenixKit's built-in one:

config :phoenix_kit,
  mailer: MyApp.Mailer

When delegation is configured, all emails will be sent through your application's mailer, allowing you to use a single mailer configuration across your entire application.

Summary

Functions

Delivers an email.

Delivers an email, raises on error.

Delivers an email using the appropriate mailer.

Delivers a list of emails.

Delivers an email via a specific Integrations connection (AWS SES, universal SMTP, or Brevo API), selected by the connection's uuid.

Gets the effective "from" email address.

Gets the effective "from" name.

Gets the mailer module to use for sending emails.

Sends an email using a template from the database.

Sends a magic link email to the user.

Functions

deliver(email, config \\ [])

@spec deliver(Swoosh.Email.t(), Keyword.t()) :: {:ok, term()} | {:error, term()}

Delivers an email.

If the email is delivered it returns an {:ok, result} tuple. If it fails, returns an {:error, error} tuple.

deliver!(email, config \\ [])

@spec deliver!(Swoosh.Email.t(), Keyword.t()) :: term() | no_return()

Delivers an email, raises on error.

If the email is delivered, it returns the result. If it fails, it raises a DeliveryError.

deliver_email(email, opts \\ [])

Delivers an email using the appropriate mailer.

If Settings key "default_email_integration_uuid" is set and resolves to an Integrations connection with valid credentials, delivery is routed through that connection via deliver_via_integration/3 (set on the core Email Sending settings page). Otherwise, if a parent application mailer is configured, delegates to it; failing that, uses the built-in PhoenixKit mailer. The setting being absent, blank, or pointing at a deleted/unconfigured connection is a no-op — behavior is unchanged from before this routing existed.

This function also integrates with the email tracking system to log outgoing emails when tracking is enabled. Recipients blocklisted by the emails module (hard bounces, complaints, manual blocks — in to, cc or bcc) are rejected before any tracking or delivery is attempted; see check_recipient_allowed/1. Send-rate limits are deliberately NOT enforced here — see the soft-dependency note at the top of this module.

deliver_many(emails, config \\ [])

@spec deliver_many(
  [
    %Swoosh.Email{
      assigns: term(),
      attachments: term(),
      bcc: term(),
      cc: term(),
      from: term(),
      headers: term(),
      html_body: term(),
      private: term(),
      provider_options: term(),
      reply_to: term(),
      subject: term(),
      text_body: term(),
      to: term()
    }
  ],
  Keyword.t()
) :: {:ok, term()} | {:error, term()}

Delivers a list of emails.

It accepts a list of %Swoosh.Email{} as its first parameter.

deliver_via_integration(email, integration_uuid, opts \\ [])

@spec deliver_via_integration(Swoosh.Email.t(), String.t(), keyword()) ::
  {:ok, term()} | {:error, term()}

Delivers an email via a specific Integrations connection (AWS SES, universal SMTP, or Brevo API), selected by the connection's uuid.

Unlike deliver_email/2, this does not go through deliver_with_runtime_config/2 — that path is hardcoded to AWS SES (config[:adapter] == Swoosh.Adapters.AmazonSES, credentials only from Provider.current().get_aws_*), so a Brevo or SMTP send routed through it would be misrouted or ignored. This function resolves the Swoosh adapter and config directly from the chosen integration's stored credentials instead, while preserving the same interception seam deliver_email/2 uses so tracking keeps working.

Returns

  • {:ok, term()} — delivered
  • {:error, {:blocked, atom()}} — a recipient (to/cc/bcc) is blocklisted by the emails module (checked before the integration is even resolved). Send-rate limits are NOT enforced here — see the module's soft-dependency note.
  • {:error, :not_configured | :deleted} — the integration uuid didn't resolve

  • {:error, {:incomplete_credentials, [String.t()]}} — the connection's status says connected, but a required field (e.g. SMTP host, SES aws_region) is blank — most likely edited after the last successful validation. Listed field names are the credential keys, never values.
  • {:error, {:unsupported_provider, String.t()} | :unsupported_provider} — the integration's provider has no known Swoosh adapter mapping (the bare atom when the credentials carry no provider key at all)

  • {:error, {:invalid_smtp_port, term()}} — the SMTP connection's port is not a number
  • {:error, :no_ca_store} — SMTP only, and a behaviour change: there is no system CA bundle, so the relay's certificate cannot be verified and the password would go out to an unauthenticated server. Sending stops. It used to proceed with verify: :verify_none, which is why slim images (distroless, scratch, some Alpine builds) never noticed they had no CA store. Install one (e.g. ca-certificates) to restore sending. A relay with no credentials to protect still degrades rather than failing.

get_from_email()

@spec get_from_email() :: String.t()

Gets the effective "from" email address.

Priority: Settings Database (runtime) > Config file (compile-time) > built-in default ("noreply@localhost"). Public so the Email Sending settings page can display the value that's actually in effect, even when no Settings override is set.

get_from_name()

@spec get_from_name() :: String.t()

Gets the effective "from" name.

Priority: Settings Database (runtime) > Config file (compile-time) > built-in default ("PhoenixKit"). Public so the Email Sending settings page can display the value that's actually in effect, even when no Settings override is set.

get_mailer()

Gets the mailer module to use for sending emails.

Returns the configured parent application mailer if set, otherwise returns the built-in PhoenixKit.Mailer.

Examples

iex> PhoenixKit.Mailer.get_mailer()
MyApp.Mailer  # if configured

iex> PhoenixKit.Mailer.get_mailer()
PhoenixKit.Mailer  # default

send_from_template(template_name, recipient, variables \\ %{}, opts \\ [])

Sends an email using a template from the database.

This is the main function for sending emails using PhoenixKit's template system. It automatically:

  • Loads the template by name
  • Renders it with provided variables
  • Tracks template usage
  • Sends the email with tracking
  • Logs to EmailSystem

Parameters

  • template_name - Name of the template in the database (e.g., "welcome_email")
  • recipient - Email address (string) or {name, email} tuple
  • variables - Map of variables to substitute in the template
  • opts - Additional options:
    • :user_uuid - Associate email with a user (for tracking)
    • :campaign_id - Campaign identifier (for analytics)
    • :from - Override from address (default: configured from_email)
    • :reply_to - Reply-to address
    • :metadata - Additional metadata map for tracking

Returns

  • {:ok, email} - Email sent successfully
  • {:error, :template_not_found} - Template doesn't exist
  • {:error, :template_inactive} - Template is not active
  • {:error, reason} - Other error

Examples

# Simple welcome email
PhoenixKit.Mailer.send_from_template(
  "welcome_email",
  "user@example.com",
  %{"user_name" => "John", "url" => "https://app.com"}
)

# With user tracking
PhoenixKit.Mailer.send_from_template(
  "password_reset",
  {"Jane Doe", "jane@example.com"},
  %{"reset_url" => "https://app.com/reset/token123"},
  user_uuid: user.uuid,
  campaign_id: "password_recovery"
)

# With metadata
PhoenixKit.Mailer.send_from_template(
  "order_confirmation",
  customer.email,
  %{"order_id" => "12345", "total" => "$99.99"},
  user_uuid: customer.uuid,
  campaign_id: "orders",
  metadata: %{order_id: order.id, amount: order.total}
)