phoenix_kit_newsletters

Newsletters module for PhoenixKit — email broadcasts and subscription management.

Installation

Add to your mix.exs:

def deps do
[
{:phoenix_kit_newsletters, "~> 0.1"}
# During development (before Hex publish):
# {:phoenix_kit_newsletters, github: "BeamLabEU/phoenix_kit_newsletters"}
]
end

Oban Setup

PhoenixKit Newsletters uses Oban for background email delivery. Add the newsletters queue to your Oban configuration:

# In config/config.exs:
config :my_app, Oban,
repo: MyApp.Repo,
queues: [newsletters: 10]

See the Oban documentation for full configuration options.

How It Works

PhoenixKit Newsletters auto-registers with PhoenixKit on startup — no additional configuration needed.

  1. Run mix deps.get
  2. Recompile PhoenixKit routes: mix deps.compile phoenix_kit --force
  3. Restart your server
  4. Enable the module in Admin > Modules > Newsletters

Migrations are managed by PhoenixKit core and applied automatically via mix phoenix_kit.update.

Requirements

DependencyVersion
Elixir~> 1.18
PhoenixKit>= 1.7.73
Phoenix LiveView~> 1.1
Oban~> 2.20
MDEx~> 0.13

Architecture

PhoenixKit Newsletters implements the PhoenixKit.Module behaviour and plugs into the host PhoenixKit app. It depends on the host for Repo, Mailer, Endpoint, Users, and Settings.

Core Schemas

All schemas use UUIDv7 primary keys.

SchemaDescription
ListNewsletter mailing list with name, slug, status
BroadcastEmail content (Markdown → HTML); status lifecycle: draft → scheduled → sending → sent
ListMemberSubscription join table (user ↔ list); unique constraint on [user_uuid, list_uuid]
DeliveryPer-recipient tracking record; status: pending → sent → delivered → opened / bounced / failed

Broadcast Sending Pipeline

Broadcaster orchestrates sending:

  1. Renders Markdown to HTML via MDEx
  2. Streams list members in batches of 500
  3. Creates Delivery records via insert_all
  4. Enqueues DeliveryWorker Oban jobs per recipient

DeliveryWorker sends individual emails with variable substitution ({{name}}, {{email}}, {{unsubscribe_url}}), optionally wraps in an email template (soft dependency on Emails module), and tracks delivery status.

Modules

ModuleRole
NewslettersMain context — CRUD for lists, members, broadcasts, deliveries
BroadcasterOrchestrates batch sending and Oban job enqueuing
DeliveryWorkerOban worker — sends individual emails, tracks delivery status
PathsCentralized path helpers — always use instead of hardcoding URLs
Web.BroadcastsAdmin LiveView — broadcasts index
Web.BroadcastEditorAdmin LiveView — create/edit broadcast with Markdown editor
Web.BroadcastDetailsAdmin LiveView — delivery stats and recipient list
Web.ListsAdmin LiveView — mailing lists index
Web.ListEditorAdmin LiveView — create/edit list
Web.ListMembersAdmin LiveView — list subscriber management
Web.UnsubscribeControllerPublic controller — token-verified unsubscribe flow
Web.RoutesPublic route definitions via route_module/0

Settings

KeyDefaultDescription
newsletters_enabledfalseEnables/disables the module
newsletters_default_templateDefault email template UUID
newsletters_rate_limit14/secDelivery rate limiting
from_emailSender email address (shared with Emails module)
from_nameSender display name (shared with Emails module)

Unsubscribe Flow

Unsubscribe tokens are signed with Phoenix.Token using the "unsubscribe" salt.

The UnsubscribeController verifies the token, performs the unsubscribe, and renders a confirmation page.

Development

mix deps.get # Install dependencies
mix test # Run all tests
mix format # Format code
mix credo # Lint / code quality
mix dialyzer # Static type checking