Developer docs
Start
Build with AI
Themes
Apps
- App development
- App development best practices
- Hooks and filters
- The App API
- Sending email from apps
- Admin bar for apps and themes
- Public routes for apps
- Admin screens, routes and UI components
- Dashboard widgets
- Data tables
- AJAX actions
- Media and uploads for apps
- App permissions
- Content types, blocks and widgets
- Content types, taxonomies and custom fields
- Search
- When an app fails: fault isolation and recovery
API and AI agents
Security and contributing
Sending email from apps
3 min read
Apps send email through the site's own settings with $this->api()->mail(), never their own SMTP connection, so their messages come from the site's address, use its template and pass the same checks as core's. This page covers the mail API, its hooks and the rules for sending from an app. Site owners setting up email want How to set up email so your site's messages arrive.
Sending
$mail = $this->api()->mail();
// In the site's template: site name, heading, your HTML, footer.
$ok = $mail->sendTemplate('owner@example.org', 'New booking', 'You have a new booking',
'<p>Anna booked <strong>Studio A</strong> for Friday at 10:00.</p>');
// Your own HTML. A plain-text part is made from it unless you pass 'text'.
$ok = $mail->send(['a@example.org', 'b@example.org'], 'Weekly report', $html, [
'reply_to' => 'support@example.org',
'from_name' => 'Northwind bookings',
]);
if (!$ok) {
$this->log('Booking email failed: ' . $mail->lastError(), [], 'warning');
}
| Method | Returns |
|---|---|
send($to, $subject, $html, $options) | true when the server accepted it. $to is one address or a list. Options: text, from, from_name, reply_to, headers (a list of Name: value lines) |
sendTemplate($to, $subject, $heading, $bodyHtml, $footNote) | the same, in the site's template; $footNote replaces the footer line |
lastError() | why the last send failed, or '' |
isConfigured() | whether the site has a sender address |
- Permission. An app that declares permissions must declare
mail.send, and check it before optional mail:if ($this->allowed('mail.send')) { … }. Without it, sends returnfalseand are logged. - Escape anything people typed before putting it in the HTML:
htmlspecialchars($name, ENT_QUOTES, 'UTF-8'). - Send outside transactions.
api()->transaction()may run its function again; mail sent inside would go twice. - Many messages? Each send waits for the server. Send in small batches from a scheduled task, and stay within your provider's limits.
- Don't change the From address to one off the site's domain unless your SMTP account may send as it; set
reply_toinstead.
Hooks
| Hook | Use |
|---|---|
mail.before_send (filter) | Receives the message as an array: to, subject, html, text, from, from_name, reply_to, headers. Return it changed, or false to stop it. |
mail.sent (action: $mail) | After the server accepted a message. |
mail.failed (action: $mail, $error) | After a failure, with the reason. |
// A staging site: deliver everything to one inbox, and mark it.
$this->addFilter('mail.before_send', function (array $mail) {
$mail['subject'] = '[staging] ' . $mail['subject'] . ' (for ' . implode(', ', $mail['to']) . ')';
$mail['to'] = ['dev-inbox@example.org'];
return $mail;
});
// Keep a record of failures in your app's log.
$this->addAction('mail.failed', function (array $mail, string $error) {
$this->log('Mail to ' . implode(', ', $mail['to']) . ' failed: ' . $error, [], 'warning');
}, 10, 2);
How core sends it
- Two methods, chosen under Settings → Email: PHP
mail(), with-fset to the From address when it is on the site's domain, or SMTP with STARTTLS or SSL,AUTH LOGINand a 15-second timeout per step. - The default From address is
noreply@the domain ofAPP_URL, withoutwww.. - There is no queue: a send happens during the request and isn't retried. Failures are logged as
ERROR: Mail send failed: … (to: …), and firemail.failed. - The SMTP password is redacted from the REST API and MCP settings, whatever the key's scopes.
Related
- The App API and App permissions.
- Hooks and filters.
- Configuration and the .env file:
APP_URLandADMIN_EMAIL.
Leave a comment