Skip to main content

Salesforce Email Architecture: Deliverability and Limits

Updated Add a comment
💬 In plain words: Sending emails from Salesforce at scale is an architecture puzzle, not just a coding task. You have to watch strict daily send limits, set up shared company email addresses, and protect your domain's reputation using security rules like SPF, DKIM, and DMARC so your messages don't end up in the spam folder. Use built-in Salesforce email for one-off alerts tied to a specific customer record, and use a dedicated marketing platform for massive promotional blasts.
📌 Real-Life Example: Imagine a customer support ticket (Case) is closed, and you want to automatically send a quick feedback survey. You can easily build this in Salesforce using a Flow or a few lines of Apex code (Messaging.SingleEmailMessage), a standard email template, and your verified support@yourcompany.com Org-Wide Address. Now, what if your marketing team wants to blast 300,000 Black Friday coupons to every lead in your database? Do not use core Salesforce or Apex for this! Daily governor limits exist specifically to keep bulk marketing blasts out of your core CRM and push them into dedicated tools like Marketing Cloud or Account Engagement (formerly Pardot).

🎯 Key Points: The Pillars of Salesforce Email

Salesforce email architecture splits cleanly into two paths: sending emails out (outbound) and receiving emails in (inbound). Knowing where each tool shines—and where it breaks—is the secret to building a stable, scalable Salesforce org.

  • 📤 Outbound Architecture: Handled by declarative Flow Email Alerts (Send Email action), custom Apex code (Messaging.SingleEmailMessage), and Org-Wide Email Addresses so messages come from a shared team inbox rather than an individual user.
  • 📥 Inbound Architecture: Handled out-of-the-box by Email-to-Case (utilizing modern Lightning Threading via headers and tokens rather than the retired Ref ID standard) or custom Apex Email Services using the Messaging.InboundEmailHandler interface when you need to parse complex emails via code.
  • 🛡️ Deliverability & Security Standards: Reaching the inbox requires strict compliance with modern email security protocols: SPF, DKIM, and DMARC. Since Google, Yahoo, and Microsoft enforced strict bulk-sender rules, failing to configure these will guarantee your Salesforce emails get blocked or sent straight to spam.
  • 🚧 Strict Governor Limits: Salesforce caps external email sends at 5,000 emails per day per org (sent via Apex or APIs to external email addresses) and allows only 10 sendEmail() method calls per Apex transaction.
  • 🆓 The Internal User Loophole: Sending emails to your own internal active User records in Salesforce does not count against your 5,000 daily external email limit—provided you pass the User's ID into setTargetObjectId() instead of hardcoding their email address as a string!
🧠 Core Takeaway: Native Salesforce email is built for 1-to-1 transactional alerts tied to a specific record. Dedicated marketing platforms are built for bulk campaigns. Regardless of which tool you use, inbox placement relies entirely on proper SPF, DKIM, and DMARC setup.

🧭 The 360 Card: Email Architecture & Deliverability

📏 The Golden Rule: Keep transactional, record-triggered notifications inside core Salesforce. Offload newsletters, cold outreach, and high-volume promotional blasts to Marketing Cloud or Account Engagement.

🎁 The Gain: Using native Salesforce email for transactional alerts gives you instant access to Lightning Email Templates, Org-Wide Addresses, automatic Activity Timeline logging, and live CRM merge fields without paying for extra middleware.

💸 The Price: Your company's domain reputation is on the line. If a badly written Flow or Apex batch job accidentally spams invalid addresses, email providers like Gmail can blacklist your entire corporate domain.

🛑 The Limits: A hard limit of 5,000 external emails per 24-hour GMT window via Apex/API, and 10 Messaging.sendEmail() calls per transaction. Always collect your email objects in a single List and invoke Messaging.sendEmail() once outside your loop.

💬 Core Q&A & Interview Prep

Q1: You need to send a templated confirmation email from Apex using a shared support address while respecting governor limits. How do you build this?

🎯 Say this first: "I would build this using Messaging.SingleEmailMessage paired with a pre-queried Org-Wide Email Address and a Lightning Email Template. To respect governor limits, I would bulkify the logic by adding every message to a collection, passing Contact or User IDs into setTargetObjectId(), and calling Messaging.sendEmail() once outside the for loop with partial success enabled."

To write production-ready email code in Apex, never hardcode the subject or body inside your class. Instead, query the EmailTemplate and OrgWideEmailAddress records before your loop starts. Inside your loop, create a Messaging.SingleEmailMessage instance for each record.

Use setTemplateId() so admins can update the email text anytime without needing a code deployment. Use setOrgWideEmailAddressId() so the customer sees a trusted address like support@yourcompany.com instead of the personal email of whichever user clicked the button.

Next, pass the recipient's Contact, Lead, or User ID into setTargetObjectId() and pass the related Case or Opportunity ID into setWhatId() so Salesforce can populate merge fields like {{{Case.CaseNumber}}}. Finally, pass false as the second parameter in Messaging.sendEmail(msgs, false). That allOrNone = false flag ensures that if one customer has a bad email address, the other 199 emails in the batch still go out!

// 1. Query Template and Org-Wide Email Address ONCE outside the loop
Id templateId = [SELECT Id FROM EmailTemplate WHERE DeveloperName = 'Case_Closed_Survey' LIMIT 1].Id;
Id owaId = [SELECT Id FROM OrgWideEmailAddress WHERE Address = 'support@company.com' LIMIT 1].Id;

// 2. Create a single list to hold all outgoing emails
List<Messaging.SingleEmailMessage> msgs = new List<Messaging.SingleEmailMessage>();

for (Case c : closedCases) {
    if (c.ContactId == null) continue; // Guard against Cases without a Contact

    Messaging.SingleEmailMessage m = new Messaging.SingleEmailMessage();
    
    // Pass Contact/Lead/User ID (never raw email strings if you can avoid it)
    m.setTargetObjectId(c.ContactId);
    
    // Link template and the Case record for merge fields
    m.setTemplateId(templateId);
    m.setWhatId(c.Id); 
    
    // Use the verified shared support address
    m.setOrgWideEmailAddressId(owaId);
    
    // Log the email as an activity on the record timeline
    m.setSaveAsActivity(true);
    
    msgs.add(m);
}

// 3. ONE bulk call outside the loop, with allOrNone set to FALSE for partial success
if (!msgs.isEmpty()) {
    Messaging.SendEmailResult[] results = Messaging.sendEmail(msgs, false);
    for (Messaging.SendEmailResult res : results) {
        if (!res.isSuccess()) {
            System.debug('Email failed to send: ' + res.getErrors()[0].getMessage());
        }
    }
}

Q2: Emails from your Salesforce org are landing in customers' spam folders or getting rejected. What deliverability settings do you check to fix this?

🎯 Say this first: "First, I would verify our domain's DNS authentication records—specifically SPF, DKIM, and DMARC alignment. Second, I would check Salesforce's internal Deliverability settings, Email Security Compliance checkboxes, and Bounce Management configuration."

Email providers like Gmail, Yahoo, and Microsoft Office 365 enforce strict authentication rules. If your org's emails are hitting spam, walk through this exact checklist:

  • 🔍 SPF (Sender Policy Framework): Check your company's public DNS TXT record. Does it include include:_spf.salesforce.com? This tells the world that Salesforce servers have permission to send emails on behalf of your domain.
  • 🔑 DKIM (DomainKeys Identified Mail): Go to Setup > DKIM Keys in Salesforce. You should generate a cryptographic key pair and publish the CNAME records in your company's DNS. DKIM adds a tamper-proof digital signature to every email headers so inbox providers know the email wasn't forged in transit.
  • 🛡️ DMARC (Domain-based Message Authentication, Reporting, and Conformance): Ensure your domain has a DMARC policy published in DNS and that your From address domain aligns with your SPF and DKIM domains. Without DMARC alignment, modern inbox filters will quarantine your messages.
  • ⚙️ Salesforce Deliverability Settings: Navigate to Setup > Deliverability. Confirm that Access to Send Email is set to All email in Production (note that newly refreshed Sandboxes default to System email only so you don't accidentally spam real customers during testing).
  • ⚠️ Sender ID Compliance Warning: Under Setup > Deliverability, watch out for the setting called "Enable compliance with standard email security mechanisms." While it helps pass legacy SPF checks by changing the envelope sender to a bounces.salesforce.com address, turning it on without setting up DKIM keys first can break strict DMARC alignment! Always activate DKIM keys as your primary authentication method.
  • 📉 Bounce Management: Turn on Activate bounce management in Deliverability settings. Repeatedly emailing dead or invalid addresses ruins your domain reputation. Bounce management automatically flags bad email addresses on the Contact or Lead record so you stop emailing them.

Q3: Design an inbound email process that creates records and handles file attachments. Should you use declarative tools or custom Apex?

🎯 Say this first: "If the emails are for customer support cases, I will always use native On-Demand Email-to-Case. I will only write a custom Apex Messaging.InboundEmailHandler if we need to create custom object records, parse structured text or JSON from the email body, or run complex validation rules that Flows cannot handle."

When designing inbound email routing, follow the standard Salesforce rule: go declarative first, and drop into code only when required.

If the requirement is routing support inquiries, use On-Demand Email-to-Case. It natively converts incoming emails into Case records, saves attachments directly as Salesforce Files (ContentVersion), and links replies to existing Cases using Lightning Threading (which relies on secure email header data and hidden tokens, replacing the legacy Ref ID format). Plus, with Record-Triggered Flows on the EmailMessage object, you can build powerful declarative automation around incoming emails without touching Apex.

However, if an external vendor emails your org automated XML/CSV reports every night, or if you need to create a custom Invoice__c record by scraping text out of the email body using regular expressions (Regex), use a custom Apex Email Service. You will write an Apex class implementing the Messaging.InboundEmailHandler interface. This gives you full access to email.plainTextBody, email.htmlBody, email.binaryAttachments, and email.textAttachments. Always enforce with sharing or check Field-Level Security (WITH USER_MODE) in your handler class so external senders can't exploit your database permissions.

Q4: Why did my Apex email count against the 5,000 daily external governor limit when I only emailed an internal company manager?

This is a classic Salesforce gotcha! If you pass an email address string into setToAddresses(new List<String>{'manager@company.com'}), Salesforce treats it as an external email and deducts it from your 5,000 daily org limit—even if that email belongs to an active Salesforce User in your org.

To send unlimited emails to internal Salesforce users without touching your daily governor limit, pass the User's 18-character Salesforce record ID directly into setTargetObjectId(userRecordId) and set setSaveAsActivity(false) (since Users cannot have activities logged directly under them like Contacts can). Salesforce immediately recognizes the User ID and processes the email for free!

Q5: When should you use a Flow "Send Email" action instead of writing Apex Messaging.SingleEmailMessage?

For 90% of standard business notifications, you should use the declarative Send Email action inside a Record-Triggered or Scheduled Flow. Modern Salesforce Flows support Lightning Email Templates, Org-Wide Email Addresses, rich text bodies, recipient collections, file attachments, and automatic activity logging on records—features that previously required Apex code.

You only need to reach for Apex Messaging.SingleEmailMessage when you have to generate dynamic PDF attachments on the fly (using Messaging.EmailFileAttachment), construct complex custom MIME headers, handle high-volume asynchronous chunks inside Database.Batchable, or programmatically inspect partial failure results using Messaging.SendEmailResult.

⚠️ INTERVIEW & ARCHITECTURE TRAP: Never propose using Batch Apex to send monthly marketing newsletters to 50,000 Contacts! Even if you split the batch jobs across multiple days, you will hit the hard 5,000 daily external email limit, lock up your transactional email queue (stopping critical password resets or order confirmations from going out), and violate Salesforce's Acceptable Use Policy. Always state clearly that bulk commercial emails belong in Marketing Cloud, Account Engagement, or an integrated third-party ESP like SendGrid or Amazon SES.

🔗 Connecting the Dots: Follow-Up

Q6: How does Salesforce "Email Relay" fit into this architecture if your corporate IT security team demands that all outbound emails pass through your company's Microsoft 365 or Google Workspace servers?

Salesforce Email Relay acts as a secure bridge between Salesforce and your corporate mail server. Normally, Salesforce sends emails directly from its own servers to your customer's inbox. However, industries like banking and healthcare often require every single outbound email to be scanned for data leaks (DLP), encrypted, or archived in the company's central email vault.

When you configure Email Relay in Setup, Salesforce stops delivering mail directly to recipients. Instead, it opens a secure, TLS-encrypted connection and hands all outbound emails straight to your company's Microsoft Exchange, Office 365, or Google Workspace SMTP relay server. Your corporate mail server runs its own security filters, stamps your company's native DKIM signature on the message, and delivers it to the customer. This keeps your IT security team happy and guarantees that Salesforce emails come from the exact same IP infrastructure as your employees' regular emails.

Q7: Now that you know how outbound and inbound emails work, how do you test your email logic in a Sandbox without accidentally emailing real customers?

When you refresh a Sandbox, Salesforce automatically sets Setup > Deliverability to "System email only" as a safety net, and appends .invalid to the end of every Contact and User email address (for example, john@doe.com.invalid). Before you switch Deliverability to "All email" in a Sandbox to test your Flows or Apex code, always run an anonymous Apex script or Data Loader batch to verify that real customer Contact and Lead email fields are masked, and only remove the .invalid suffix from the specific test records owned by your QA team!

Was this article helpful?

Have a question or spotted a mistake? Ask here about this article

Found this guide useful? Buy me a coffee (opens in a new tab)

Comments

Questions, corrections and your own experience are all welcome.