Install
This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.
Install straight from GitHub with WP-CLI:
wp plugin install https://github.com/miroslav-balan-at/mail-chronicle/archive/refs/heads/main.zipProfessional email logging plugin for WordPress with multi-provider support, real-time event tracking, and a React-based admin interface.
Features
- Automatic Email Logging — captures all outgoing WordPress emails via
wp_mail - Multi-Provider Support — Mailgun, WordPress default; SendGrid-ready via enum + one
case - Event Tracking — delivery, opens, clicks, bounces via Mailgun webhooks or background sync
- React Admin UI — filterable table, detail modal with event timeline, Sync Latest button
- REST API — full programmatic access with
manage_optionsauth - Webhook Support — HMAC-SHA256 verified real-time status updates from Mailgun
- Background Sync — cursor-based WP-Cron sync picks up where it left off
- Translation Ready — all strings pass through
__()/_e() - Dependency Injection — all feature handlers depend on interfaces, wired via a central
ServiceProvider - Developer Hooks — 12 actions and 4 filters for extension without modifying core files
Requirements
- WordPress 6.0+
- PHP 8.1+
- Composer
- Node.js & npm (for asset builds only)
Installation
From GitHub
cd wp-content/plugins
git clone https://github.com/miroslav-balan-at/mail-chronicle.git
cd mail-chronicle
composer install --no-dev
npm ci && npm run build
Activate the plugin in Plugins → Installed Plugins.
From WordPress.org
Search for Mail Chronicle in Plugins → Add New.
Configuration
- Go to Mail Chronicle → Settings
- Set Provider (Mailgun or WordPress)
- Configure log retention period
Mailgun Setup
- Enter your API key and domain in Settings
- Set the webhook URL in your Mailgun dashboard:
https://your-site.com/wp-json/mail-chronicle/v1/webhook/mailgun - Enable the events you want to track (delivered, opened, clicked, bounced, etc.)
- Optionally configure the background sync interval (default: every 10 minutes)
REST API
Base path: /wp-json/mail-chronicle/v1/
All endpoints require manage_options except the webhook endpoint (HMAC-SHA256 verified).
| Method | Endpoint | Description |
|---|---|---|
GET |
/emails |
Paginated list with filters |
GET |
/emails/{id} |
Single email |
GET |
/emails/{id}/events |
Event timeline |
DELETE |
/emails/{id} |
Delete one email |
DELETE |
/emails |
Delete all emails |
GET |
/settings |
Current settings |
POST |
/sync |
Trigger provider sync |
POST |
/webhook/mailgun |
Mailgun event receiver |
Filter Parameters (GET /emails)
| Parameter | Type | Description |
|---|---|---|
per_page |
int | Results per page (default: 20) |
page |
int | Page number |
orderby |
string | Sort field (default: sent_at) |
order |
string | Sort direction: ASC or DESC (default: DESC) |
status |
string | Filter by status |
provider |
string | Filter by provider |
search |
string | Search recipient/subject |
date_from |
string | Start date (Y-m-d) |
date_to |
string | End date (Y-m-d) |
domain |
string | Filter by sender domain |
Developer Hooks
Actions
| Hook | Description |
|---|---|
mail_chronicle_email_logging |
Fires immediately before the email row is inserted |
mail_chronicle_after_email_logged |
Fires after email row is inserted |
mail_chronicle_email_status_updated |
Fires after status is set to sent/failed |
mail_chronicle_before_email_deleted |
Fires before single email is deleted |
mail_chronicle_after_email_deleted |
Fires after single email is deleted |
mail_chronicle_before_all_emails_deleted |
Fires before all emails are truncated |
mail_chronicle_after_all_emails_deleted |
Fires after all emails are truncated |
mail_chronicle_after_settings_saved |
Fires after settings are saved |
mail_chronicle_before_webhook_processed |
Fires before webhook payload is processed |
mail_chronicle_after_webhook_processed |
Fires after webhook is processed |
mail_chronicle_before_sync |
Fires before provider sync |
mail_chronicle_after_sync |
Fires after provider sync |
Filters
| Hook | Description |
|---|---|
mail_chronicle_before_email_logged |
Filter/suppress email data before logging |
mail_chronicle_get_emails_args |
Filter query arguments before DB fetch |
mail_chronicle_get_emails |
Filter results after DB fetch |
mail_chronicle_before_settings_saved |
Filter validated settings before saving |
Example — suppress logging for a specific address:
add_filter( 'mail_chronicle_before_email_logged', function( array $data ): array {
if ( str_contains( $data['to'], 'no-log@example.com' ) ) {
return [];
}
return $data;
} );
Adding a New Email Provider
- Add a
casetosrc/Common/Entities/Email_Provider.php - Create
src/Features/SyncFrom<Provider>/SyncFrom<Provider>.phpwith ahandle(array $args): arraymethod - Add one
casetoSyncController::dispatch() - Optionally create
src/Features/Process<Provider>Webhook/for webhook support
The sync endpoint, settings UI, and admin interface adapt automatically.
Development
# Install dependencies
composer install
npm ci && npm run build
# Run all tests
composer test
# Static analysis (level 10)
composer phpstan
# Code standards
composer phpcs
composer phpcbf
# Watch assets
npm start
Architecture
Vertical Slice Architecture — each feature is fully self-contained in src/Features/<FeatureName>/:
src/
├── Common/
│ ├── Database/ # Schema (dbDelta wrapper)
│ ├── Entities/ # Email, ProviderEvent, enums
│ ├── Infrastructure/ # Wpdb-backed repository implementations
│ ├── Query/ # EmailQuery value object
│ ├── Repository/ # EmailRepositoryInterface, ProviderEventRepositoryInterface
│ └── WordPress/ # Activator, Deactivator, HooksLoader
├── Features/
│ ├── DeleteEmail/
│ ├── FetchStoredContent/
│ ├── GetEmails/
│ ├── LogEmail/
│ ├── ManageSettings/
│ ├── ProcessMailgunWebhook/
│ ├── PurgeOldLogs/
│ ├── Sync/
│ └── SyncFromMailgun/
├── Plugin.php
├── ServiceContainer.php
└── ServiceProvider.php # Composition root — all wiring lives here
Database tables: {prefix}mail_chronicle_logs, {prefix}mail_chronicle_events
Contributing
See CONTRIBUTING.md.
Changelog
See CHANGELOG.md.
License
GPL v2 or later — see LICENSE.
Author
Miroslav Balan · hygienemitsystem.at · miroslav@balan.at