Magic-Mail — Smart Routing
How it works
For every outgoing email Magic-Mail:
- Evaluates each active routing rule in priority order (highest first).
- Picks the first rule whose condition matches.
- Uses the rule's primary account. If it's rate-limited or failing, tries the fallback chain.
- If no rule matches, falls back to the account with the highest global priority.
- Tracks rate limits per account in real time (per-minute, per-hour, per-day).
- Logs every attempt to the Email Logs table with status, response time, and error detail.
Creating a rule
In Strapi admin → Magic-Mail → Routing Rules → Add Rule:
| Field | Description |
|---|---|
| Name | Human-readable identifier (e.g. "Marketing via SendGrid") |
| Condition | JavaScript-like expression evaluated against the email object |
| Account | Primary account to use |
| Fallback chain | Ordered list of backup accounts |
| Priority | Higher numbers evaluated first (0–100) |
| Active | Toggle to disable without deleting |
Condition syntax
Conditions are simple JS-style predicates. Available variables:
type— email type:'transactional' | 'marketing' | 'notification' | 'system'to— recipient email (string)from— sender email (string)subject— subject line (string)priority— email priority:'high' | 'normal' | 'low'customField— arbitrary metadata string passed by the caller
Supported operators: ===, !==, .startsWith(), .endsWith(), .includes(), &&, ||.
Examples:
type === 'marketing'
to.endsWith('@enterprise.com')
from.includes('newsletter')
priority === 'high' && type === 'transactional'
customField === 'tenant-abc'Worked examples
Example 1: Route by email type
Name: Marketing via SendGrid
Condition: type === 'marketing'
Account: SendGrid (API Key)
Fallback: Mailgun, then SMTP
Priority: 80Sends all marketing emails via SendGrid. If SendGrid hits its rate limit or fails, tries Mailgun, then SMTP.
Example 2: Route by recipient domain
Name: Enterprise customers
Condition: to.endsWith('@enterprise.com') || to.endsWith('@enterprise.de')
Account: Microsoft 365 (OAuth)
Fallback: Gmail OAuth
Priority: 90Enterprise customers get Microsoft 365 (higher deliverability for B2B). Fallback to Gmail.
Example 3: Combine conditions
Name: Critical transactional
Condition: type === 'transactional' && priority === 'high'
Account: SendGrid
Fallback: Microsoft 365, Gmail
Priority: 100For password resets, order confirmations, etc.: highest priority, triple-redundant.
Example 4: Route by custom field
Name: Tenant A
Condition: customField === 'tenant-a'
Account: Tenant A Gmail
Priority: 50Multi-tenant SaaS pattern — each tenant sends from its own account.
// Trigger from code
await strapi.plugin('email').service('email').send({
to: user.email,
subject: 'Invoice',
html: template,
customField: `tenant-${user.tenantId}`,
});Fallback chains
A fallback chain is a second-chance ordered list. Magic-Mail tries the primary account first. If it's:
- Rate-limited (would exceed per-minute/hour/day limit), OR
- Authentication error (OAuth token expired, API key invalid), OR
- Connection error (provider unreachable)
...it moves to the next account in the chain. Only after the chain is exhausted does it return an error.
Each fallback attempt is logged separately, so you can see exactly which account delivered the email.
Priority tie-breakers
If two rules have the same priority and both match, the rule created first (lowest database ID) wins. Keep priorities unique for determinism.
Global priority (no rule matches)
If no routing rule matches, Magic-Mail uses the account with the highest account priority. Set this on each account in Email Accounts → Edit.
Typical setup:
Gmail OAuth — priority 100 (default for unclassified emails)
SMTP Backup — priority 50 (last resort)Rate limits
Configure per-account limits in Email Accounts → Edit → Rate Limits:
Gmail: 500/day, 50/hour, 5/minute
SendGrid: 10,000/day, 1,000/hour, 100/minute
SMTP: unlimitedMagic-Mail consults Redis (if configured) or the database to enforce limits across multiple Strapi instances.
Testing rules
Use the Test Rule button in the admin UI. Paste a sample email payload; Magic-Mail shows which rule would match and which account would be picked.
Programmatic test:
const router = strapi.plugin('magic-mail').service('email-router');
const selected = await router.selectAccount({
to: 'user@example.com',
type: 'marketing',
subject: 'Newsletter',
});
console.log(selected.accountName);Best practices
- Separate rules by email type (transactional, marketing, notification, system). This keeps logic auditable.
- Always define a fallback for business-critical emails.
- Use descriptive names — you will see them in logs.
- Reserve priority 100+ for critical/safety rules that should never be overridden.
- Deactivate, don't delete — use the Active toggle when testing.
- Monitor the logs to see which rules actually fire and which accounts bear most load.
Next: API Reference →