Current section
Files
Jump to
Current section
Files
phoenix_kit_emails
README.md
README.md
# PhoenixKitEmails
[](https://elixir-lang.org)
[](LICENSE.md)
Email tracking, analytics, and AWS SES integration for [PhoenixKit](https://github.com/BeamLabEU/phoenix_kit). Drop-in email logging, delivery event tracking, engagement metrics, templating, rate limiting, and an admin dashboard.
## Features
- **Email logging** — comprehensive logging of all outgoing emails with optional body/header storage
- **AWS SES integration** — SQS-based event polling for delivery, bounce, complaint, open, and click events
- **Analytics & metrics** — system stats, engagement rates, campaign performance, and provider comparison
- **Email templates** — create and manage reusable email templates with a LiveView editor
- **Rate limiting** — configurable protection against abuse and spam via [Hammer](https://github.com/ExHammer/hammer)
- **Blocklist** — manage blocked email addresses
- **Queue management** — view and manage outgoing email queue
- **Archival** — automatic cleanup, body compression, and S3 archival of old email data
- **Webhook processing** — process incoming delivery/bounce/complaint webhooks
- **CSV export** — export email logs via controller endpoint
- **Email open/click tracking** — pixel tracking and link rewriting for engagement metrics
- **Admin dashboard** — LiveView pages for logs, metrics, templates, queue, blocklist, and settings
- **Auto-discovery** — implements `PhoenixKit.Module` behaviour; PhoenixKit finds it at startup with zero config
## ⚠️ Email templates are moving to files
Message templates are moving out of `phoenix_kit_email_templates` and into
files a host owns in its own repository — version-controlled and reviewable,
rather than edited through an admin UI and invisible to code review. Core reads
them through `phoenix_kit_templates`.
**Nothing has changed yet.** The database layer still wins at send time and
every customized template works exactly as it does today. But a later release
drops the table, and **edits that have not been exported will be lost then**.
If you have customized any email template:
```bash
mix phoenix_kit_emails.templates.export # --dry-run to preview first
```
Review the diff and commit the files. Nothing is deleted and no row is touched,
so an export that turns out wrong costs nothing but the files.
The task exports **only what you actually edited**. A template still identical
to what this package ships is skipped, because core supplies those itself now,
translated into every shipped locale. On an install that never opened the
template editor it writes nothing at all, and you can stop reading here.
Newsletter layouts (`is_system: false`, picked per broadcast) are not affected
— those are authored at runtime by people who cannot open a pull request, so
they keep a table and an editor.
### What the files look like
The template name becomes a directory; each part is a file, and any locale that
is not the fallback carries its code:
```
priv/phoenix_kit_templates/
└── register/
├── subject.txt # the fallback locale — what everyone falls through to
├── subject.uk.txt
├── text.txt
└── html.html
```
## Installation
Add `phoenix_kit_emails` to your dependencies in `mix.exs`:
```elixir
def deps do
[
{:phoenix_kit_emails, "~> 0.2"}
]
end
```
Then fetch dependencies:
```bash
mix deps.get
```
> **Note:** For development or if not yet published to Hex, you can use:
> ```elixir
> {:phoenix_kit_emails, github: "BeamLabEU/phoenix_kit_emails"}
> ```
PhoenixKit auto-discovers the module at startup — no additional configuration needed.
## Quick Start
1. Add the dependency to `mix.exs`
2. Run `mix deps.get`
3. Enable the module in admin settings (`email_enabled: true`)
4. Configure your AWS SES configuration set name in settings
5. Email logs and analytics are available at `/admin/emails`
## Usage
### System management
```elixir
alias PhoenixKit.Modules.Emails
# Check if system is enabled
Emails.enabled?()
# Enable/disable the system
Emails.enable_system()
Emails.disable_system()
# Get current configuration
config = Emails.get_config()
```
### Email logging
```elixir
# Create a log entry
{:ok, log} = Emails.create_log(%{
to: "user@example.com",
from: "noreply@example.com",
subject: "Welcome!",
status: "sent"
})
# List logs with filters
logs = Emails.list_logs(%{status: "sent", page: 1})
# Get a specific log
log = Emails.get_log!(log_uuid)
```
### Analytics
```elixir
# Get system statistics
stats = Emails.get_system_stats(:last_30_days)
# => %{total_sent: 5000, delivered: 4850, bounce_rate: 2.5, open_rate: 23.4}
# Get campaign performance
campaign_stats = Emails.get_campaign_stats("newsletter_2024")
# => %{total_sent: 1000, delivery_rate: 98.5, open_rate: 25.2, click_rate: 4.8}
# Get engagement metrics
metrics = Emails.get_engagement_metrics(:last_7_days)
# Get provider performance comparison
performance = Emails.get_provider_performance(:last_30_days)
```
### Webhook processing
```elixir
# Process an incoming webhook from AWS SES
{:ok, event} = Emails.process_webhook_event(webhook_data)
# List events for a specific log
events = Emails.list_events_for_log(log_uuid)
```
### Maintenance
```elixir
# Clean up old logs (by retention days)
{deleted_count, _} = Emails.cleanup_old_logs(90)
# Compress old email bodies to save storage
Emails.compress_old_bodies(30)
# Archive old data to S3
Emails.archive_to_s3(180)
```
## Settings
Settings are managed through the PhoenixKit Settings API and can be configured via the admin UI at `/admin/settings/email-sending`.
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `email_enabled` | boolean | `false` | Enable/disable the email system |
| `email_save_body` | boolean | `false` | Save full email body (vs preview only) |
| `email_save_headers` | boolean | `false` | Save email headers |
| `email_ses_events` | boolean | `false` | Manage AWS SES delivery events |
| `email_retention_days` | integer | `90` | Days to keep email logs |
| `aws_ses_configuration_set` | string | — | AWS SES configuration set name |
| `email_compress_body` | integer | — | Compress body after N days |
| `email_archive_to_s3` | boolean | `false` | Enable S3 archival |
| `email_s3_bucket` | string | — | S3 bucket archives are written to |
| `email_s3_integration` | string | — | Integrations UUID that signs uploads (unset = ExAws chain) |
| `email_sampling_rate` | integer | — | Percentage of emails to fully log |
| `email_create_placeholder_logs` | boolean | `false` | Create placeholder logs for orphaned events |
## Permissions
The module declares permissions via `permission_metadata/0`:
- `"emails"` — access to email admin dashboard and all sub-pages
Use `Scope.has_module_access?/2` to check permissions in your application.
## Architecture
```
lib/
mix/tasks/
phoenix_kit_emails.install.ex # Install mix task
phoenix_kit/modules/emails/
emails.ex # Context + PhoenixKit.Module behaviour
application_integration.ex # Application-level integration
archiver.ex # S3 archival logic
archive_worker.ex # Hourly Oban cron that calls the archiver
email_log_data.ex # Email log data struct
event.ex # Delivery/bounce/complaint event schema
interceptor.ex # Email interceptor for logging
log.ex # Email log schema
metrics.ex # Analytics and metrics queries
paths.ex # Centralized URL path helpers
provider.ex # Provider behaviour
rate_limiter.ex # Rate limiting via Hammer
sqs_polling_job.ex # Oban job for SQS polling
sqs_polling_manager.ex # SQS polling lifecycle management
sqs_processor.ex # SQS message processing
supervisor.ex # OTP Supervisor
table_columns.ex # Admin table column definitions
template.ex # Email template schema
templates.ex # Template management context
utils.ex # Shared utilities
web/
blocklist.ex # Blocklist admin LiveView
details.ex # Email log detail LiveView
emails.ex # Email logs admin LiveView
email_tracking.ex # Open/click tracking
export_controller.ex # CSV export controller
metrics.ex # Metrics dashboard LiveView
queue.ex # Queue management LiveView
routes.ex # Route definitions
settings.ex # Settings admin LiveView
template_editor.ex # Template editor LiveView
templates.ex # Templates admin LiveView
webhook_controller.ex # Webhook endpoint controller
```
### Database Tables
| Table | Description |
|-------|-------------|
| `phoenix_kit_email_logs` | Email log records (UUIDv7 PK) |
| `phoenix_kit_email_events` | Delivery/bounce/complaint/open/click events |
| `phoenix_kit_email_templates` | Reusable email templates |
## Development
```bash
mix deps.get # Install dependencies
mix test # Run tests
mix format # Format code
mix credo --strict # Static analysis (strict mode)
mix dialyzer # Type checking
mix docs # Generate documentation
mix precommit # Compile + format + credo + dialyzer
mix quality # Format + credo + dialyzer
```
## Troubleshooting
### Emails not appearing in admin
- Verify `email_enabled` is `true` in settings
- Ensure the module is listed as a dependency in the parent app's `mix.exs`
### SES events not processing
- Verify `email_ses_events` is enabled in settings
- Check that `aws_ses_configuration_set` is configured
- Ensure SQS queue permissions allow the application to poll
- Review Oban dashboard for failed SQS polling jobs
### Metrics showing zero data
- Confirm emails are being logged (check `/admin/emails`)
- Verify the date range filter matches when emails were sent
## License
MIT — see [LICENSE](LICENSE.md) for details.
## Links
- [GitHub](https://github.com/BeamLabEU/phoenix_kit_emails)
- [PhoenixKit](https://github.com/BeamLabEU/phoenix_kit)