Email Provider
New in 2.3.6— Plugins can take over outbound email delivery for the entire site, routing all system emails through an external service (Mailchimp Transactional, SendGrid, Postmark, etc.) instead of the built-in PHP mailer.
Declaring the Capability
In plugin_info.json, declare that your plugin can handle email:
"capabilities": {
"email": ["core", "self"]
}
| Value | Meaning |
|---|---|
"self" |
Plugin sends its own emails only (order confirmations, notifications, etc.) |
"core" |
Plugin can also handle all core system emails (contact forms, password resets, Shield activation, etc.) |
Use ["core", "self"] if your plugin also sends its own transactional emails. Use ["core"] if it only acts as a transport layer.
Implementing EmailProviderInterface
Your plugin (or a dedicated service class) must implement App\Interfaces\EmailProviderInterface:
<?php
namespace Plugins\MailchimpMailer;
use App\Interfaces\EmailProviderInterface;
class MailchimpProvider implements EmailProviderInterface
{
public function handleEmail(array $data): bool
{
// $data keys:
// to string[] Recipient email addresses
// from string Sender email address
// fromName string Sender display name
// subject string Email subject
// body string HTML or plain text body
// altMessage string Plain text alt body (for HTML emails)
// cc string[] CC addresses
// bcc string[] BCC addresses
// replyTo string|null Reply-to address
// replyToName string|null Reply-to display name
// attachments array CI4 native attachment structure
$apiKey = setting('MailchimpMailer.apiKey');
if (empty($apiKey)) {
// Not configured — fall through to core mailer
return false;
}
try {
$this->sendViaMandrill($apiKey, $data);
return true; // Handled — core mailer is skipped
} catch (\Throwable $e) {
log_message('error', 'MailchimpMailer: send failed — ' . $e->getMessage());
return false; // Fall through to core as a safety net
}
}
}
Return values:
true— your plugin handled delivery. The core PHP/SMTP mailer is skipped.false— your plugin did not handle it (unconfigured, or error). Core mailer runs as normal.
Always return false gracefully when unconfigured so that email still works on a fresh install before the plugin is configured.
Registering in register()
Call service('emailProvider')->register() from your plugin's register() method:
public function register(): void
{
service('emailProvider')->register($this->getSlug(), new MailchimpProvider());
}
EmailProviderService is a shared CI4 service. register() is called once per request at boot time, before any controller runs.
Admin → Settings → Email
When your plugin is active and registered, it appears in the Email Provider dropdown at Admin → Settings → Email. The admin can switch between Core (default) and your plugin at any time without deactivating it.
Activation Modal
When your plugin is the first plugin declaring "core" email capability to be activated, the admin sees a prompt:
"This plugin can handle core system emails (contact forms, password resets, etc.) as well as its own. Do you want it to take over core email delivery?"
Subsequent "core"-capable plugins do not show the modal again — the existing provider selection is preserved.
Checklist
- [ ]
capabilities.emaildeclared inplugin_info.json - [ ]
EmailProviderInterfaceimplemented - [ ] Provider registered in
register() - [ ]
handleEmail()returnsfalsegracefully when unconfigured