email-service-inbound
Inbound email processing in Salesforce via Email Services + the `Messaging.InboundEmailHandler` Apex interface. Covers EmailService configuration (running user, accept-from address, attachment handling, error / failure routing), the EmailServicesAddress per-routing-address pattern, the handler's `Messaging.InboundEmail` payload (text body, HTML body, headers, attachments, in-reply-to threading), and the canonical Email-to-Case alternative for case creation. NOT for outbound email (use admin/email-templates-and-alerts), NOT for Email-to-Case flow customization itself (use service/email-to-case).
Best use case
email-service-inbound is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Inbound email processing in Salesforce via Email Services + the `Messaging.InboundEmailHandler` Apex interface. Covers EmailService configuration (running user, accept-from address, attachment handling, error / failure routing), the EmailServicesAddress per-routing-address pattern, the handler's `Messaging.InboundEmail` payload (text body, HTML body, headers, attachments, in-reply-to threading), and the canonical Email-to-Case alternative for case creation. NOT for outbound email (use admin/email-templates-and-alerts), NOT for Email-to-Case flow customization itself (use service/email-to-case).
Teams using email-service-inbound should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/email-service-inbound/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How email-service-inbound Compares
| Feature / Agent | email-service-inbound | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Inbound email processing in Salesforce via Email Services + the `Messaging.InboundEmailHandler` Apex interface. Covers EmailService configuration (running user, accept-from address, attachment handling, error / failure routing), the EmailServicesAddress per-routing-address pattern, the handler's `Messaging.InboundEmail` payload (text body, HTML body, headers, attachments, in-reply-to threading), and the canonical Email-to-Case alternative for case creation. NOT for outbound email (use admin/email-templates-and-alerts), NOT for Email-to-Case flow customization itself (use service/email-to-case).
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
Related Guides
SKILL.md Source
# Inbound Email Service
Salesforce can accept inbound email and run Apex against each
message. The mechanism: an **Email Service** (org-level
configuration) maps one or more **Email Service Addresses** (the
local-part + Salesforce-supplied domain) to a class implementing
`Messaging.InboundEmailHandler`. Apex receives the parsed email,
returns a `Messaging.InboundEmailResult`, and the platform
responds (delivery success, error reply, drop) accordingly.
The classic alternative is **Email-to-Case** — a built-in service
that creates a Case from each email, with extensive admin
configuration (auto-response, routing, threading, contact lookup).
Email-to-Case is the right answer for case creation; custom email
service is the right answer for everything else (file uploads,
custom-object creation, audit logging, complex routing).
What this skill is NOT. Outbound email — `admin/email-templates-and-alerts`.
Email-to-Case-specific configuration — `service/email-to-case`.
This skill is the custom-handler path.
---
## Before Starting
- **Decide custom service vs Email-to-Case.** Case creation? E2C
is built; don't reinvent. Anything else? Custom service.
- **Plan the running user.** The handler runs as the user
configured on the Email Service. Their permissions determine
what the handler can do. Use a dedicated integration user.
- **Plan the accept-from policy.** Anonymous public addresses
receive spam; consider authorized-senders allow-listing or
rate limiting.
- **Plan attachment handling.** Salesforce has limits (max email
size, max attachment size, total org file storage). Decide
store vs discard before traffic ramps.
---
## Core Concepts
### Email Service vs Email Services Address
- **Email Service** (org-level) — the configuration: running
user, max retention, accept-from policy, accept attachments,
error response template.
- **Email Services Address** (per address) — the actual local-part
+ Salesforce-supplied subdomain. One Email Service can have
many Addresses (e.g. `quotes-prod@...`, `quotes-dev@...`,
`quotes-eu@...`), all routing to the same handler.
The handler doesn't see which Address received the email
*directly* — it sees the recipient in `email.toAddresses`. Branch
on the recipient if you need per-address logic.
### `Messaging.InboundEmailHandler` interface
```apex
global class IncomingQuoteHandler implements Messaging.InboundEmailHandler {
global Messaging.InboundEmailResult handleInboundEmail(
Messaging.InboundEmail email,
Messaging.InboundEnvelope envelope
) {
Messaging.InboundEmailResult result = new Messaging.InboundEmailResult();
try {
processQuote(email);
result.success = true;
} catch (Exception ex) {
result.success = false;
result.message = 'Could not process: ' + ex.getMessage();
ApplicationLogger.error('Quote email failed', ex);
}
return result;
}
}
```
Three things to know:
1. **`global` is required.** Same as `SandboxPostCopy`.
2. **`InboundEmail` payload** — `subject`, `fromAddress`,
`fromName`, `toAddresses`, `ccAddresses`, `plainTextBody`,
`htmlBody`, `headers` (a list, not a map — the `references` /
`inReplyTo` headers are critical for threading), `binaryAttachments`,
`textAttachments`.
3. **Return value.** `success = true` → delivery confirmed.
`success = false` + `message` → bounce-back response. The
`message` is what the sender sees.
### Email threading via `In-Reply-To` and `References` headers
Email clients thread replies by `Message-Id` and `In-Reply-To`
headers. Salesforce's email service parses them in `email.headers`:
```apex
String inReplyTo = null;
for (Messaging.InboundEmail.Header h : email.headers) {
if (h.name.toLowerCase() == 'in-reply-to') {
inReplyTo = h.value;
break;
}
}
```
For threading inbound emails to existing Salesforce records:
- **Email-to-Case threading** — uses `[ref:...]` token in subject /
body. The system inserts the token in outbound replies; inbound
replies preserve it; E2C extracts it and links the email to the
existing case.
- **Custom service threading** — implement your own. Either embed
a token in your outbound emails (case-insensitive, robust against
client mangling) or look up by `In-Reply-To` against a stored
Message-Id of your previous outbound.
### Attachment handling
```apex
for (Messaging.InboundEmail.BinaryAttachment att : email.binaryAttachments) {
ContentVersion cv = new ContentVersion(
Title = att.fileName,
PathOnClient = att.fileName,
VersionData = att.body,
FirstPublishLocationId = parentRecordId
);
insert cv;
}
```
Limits:
- **Max email size**: ~25 MB total (configured per Email Service;
email-with-attachments above this are bounced).
- **Max single attachment**: limited by the email-size cap.
- **Org-wide file storage**: every saved attachment counts against
the org's File Storage allocation. Plan retention.
### Email-to-Case vs custom service decision
| Need | Use |
|---|---|
| Create a Case from an email | **Email-to-Case** |
| Auto-response from a template | **Email-to-Case** (or On-Demand E2C) |
| Threading replies to existing Case | **Email-to-Case** with `[ref:...]` token |
| Create a Lead / Opportunity / Custom Object | **Custom service** |
| Upload a file to a record | **Custom service** |
| Trigger a downstream API callout | **Custom service** |
| Complex routing (e.g. "if subject starts with X, do Y") | **Custom service** (or E2C with assignment rules — depends) |
| Multi-language / encoded payloads | **Custom service** for full control |
---
## Common Patterns
### Pattern A — Lead-from-email-form
**When to use.** Marketing landing page submits a form via email
to a known address; need to create a Lead from each.
```apex
global class LeadFromEmail implements Messaging.InboundEmailHandler {
global Messaging.InboundEmailResult handleInboundEmail(
Messaging.InboundEmail email, Messaging.InboundEnvelope envelope
) {
Messaging.InboundEmailResult res = new Messaging.InboundEmailResult();
try {
Lead l = new Lead(
Email = email.fromAddress,
LastName = email.fromName != null ? email.fromName : '(unknown)',
Company = parseCompanyFromBody(email.plainTextBody),
LeadSource = 'Email Form'
);
insert l;
res.success = true;
} catch (DmlException ex) {
res.success = false;
res.message = 'Could not create lead: ' + ex.getMessage();
}
return res;
}
}
```
### Pattern B — File upload to existing record via subject token
**When to use.** Users email attachments to a routing address with
the record Id in the subject (`Upload — 0061a000007ABC`).
The handler parses the Id from the subject, validates it, attaches
files. Returns success / failure to the sender.
### Pattern C — Anti-spam allow-list
**When to use.** Public-facing routing address that gets spam.
```apex
private static final Set<String> ALLOWED_DOMAINS = new Set<String>{
'acme.com', 'partner.example.com'
};
global Messaging.InboundEmailResult handleInboundEmail(
Messaging.InboundEmail email, Messaging.InboundEnvelope envelope
) {
String fromDomain = email.fromAddress.substringAfter('@').toLowerCase();
if (!ALLOWED_DOMAINS.contains(fromDomain)) {
Messaging.InboundEmailResult res = new Messaging.InboundEmailResult();
res.success = false;
res.message = 'Sender domain not authorized';
return res;
}
// ... legitimate processing ...
}
```
For more nuanced allow-listing, store the list in Custom Metadata
or Custom Setting so admins can manage without redeploying Apex.
---
## Decision Guidance
| Situation | Approach | Reason |
|---|---|---|
| Create a Case from email | **Email-to-Case** | Built-in; threading, routing, auto-response included |
| Create any other record from email | **Custom Email Service + InboundEmailHandler** | E2C only creates Cases |
| Public address receives spam | **Custom service with allow-list** | E2C also has spam handling but per-Case |
| Need to upload files | **Custom service** | E2C Email Message attachments tied to Case |
| Threading replies to existing record | **Subject token** (`[ref:...]`) or `In-Reply-To` header parsing | Email clients mangle these; tokens are more robust |
| Inbound volume > 1K / day per address | **Plan governor budget** + dedicated running user | High volume can hit Apex governor in a single batch |
| Multi-language / RTL / encoded subject | **Custom handler with explicit charset handling** | E2C parses for the common cases; edge cases break it |
| Inbound email triggers a callout | **Custom service** + Platform Event for async work | Don't do the callout in the handler synchronously |
| Anonymous public access undesirable | **Email Service `Authorize Email Addresses`** | Per-Service allow-list at the platform level |
---
## Recommended Workflow
1. **Decide custom service vs Email-to-Case.** Case creation → E2C; everything else → custom.
2. **Provision a dedicated running user** for the Email Service. Document its required permissions.
3. **Implement `Messaging.InboundEmailHandler`** with the right business logic.
4. **Configure Email Service** in Setup → Email → Email Services. Set running user, max email size, accept attachments, error-response template.
5. **Create one or more Email Services Addresses.** Each gets a Salesforce-supplied subdomain.
6. **Plan threading** if applicable (subject token, `In-Reply-To` parse).
7. **Test by sending real emails** to the address. Verify success / failure paths.
8. **Monitor.** Inbound email volume, handler exceptions, attachment storage growth.
---
## Review Checklist
- [ ] Class implements `Messaging.InboundEmailHandler` with `global` access.
- [ ] Handler returns `InboundEmailResult` with explicit `success` value (never silently throws).
- [ ] Running user is dedicated to the Email Service, with documented permissions.
- [ ] Allow-list / spam handling for public-facing addresses.
- [ ] Attachment retention policy explicit.
- [ ] Threading via subject token or `In-Reply-To` if applicable.
- [ ] Test class covers success path, malformed input, attachment-too-large, allow-list-rejection.
- [ ] Email Service `Max Email Size` and `Accept Attachments` configured deliberately.
---
## Salesforce-Specific Gotchas
1. **`Messaging.InboundEmailHandler` requires `global` access.** Same as `SandboxPostCopy`. (See `references/gotchas.md` § 1.)
2. **`email.headers` is a `List<Header>`, not a `Map`.** Iterate to find the header you want. (See `references/gotchas.md` § 2.)
3. **Returning `success = false`** triggers a bounce-back to the sender. The `message` is what the sender sees — don't expose stack traces. (See `references/gotchas.md` § 3.)
4. **Max email size has a hard ceiling**; emails above are bounced before the handler sees them. (See `references/gotchas.md` § 4.)
5. **Email Services Address subdomain is Salesforce-supplied**, not customer-domain-mappable. For customer domains, use email forwarding from the customer side. (See `references/gotchas.md` § 5.)
6. **The handler runs in the configured running user's context**, not the sender's. FLS / sharing applies to that user. (See `references/gotchas.md` § 6.)
7. **In-Reply-To / Message-Id headers are unreliable** across email clients; subject-token threading is more robust. (See `references/gotchas.md` § 7.)
---
## Output Artifacts
| Artifact | Description |
|---|---|
| `Messaging.InboundEmailHandler` class | The handler implementation |
| Test class | Covers handler with synthetic `InboundEmail` payloads |
| Email Service configuration | Documented setup steps in Setup → Email → Email Services |
| Allow-list source | Custom Metadata / Custom Setting for admin-managed sender list |
| Threading strategy | Subject token format or `In-Reply-To` parsing logic |
---
## Related Skills
- `service/email-to-case` — when the requirement is case creation; this skill is the custom-service alternative.
- `admin/email-templates-and-alerts` — outbound email infrastructure.
- `apex/apex-event-bus-subscriber` — when the handler publishes a Platform Event for async downstream work.
- `apex/dynamic-apex` — when the handler needs Schema describe to create records of varying types.Related Skills
service-account-credential-rotation
Use when designing credential rotation for integration users, connected apps, named credentials, and OAuth client secrets in Salesforce. Covers rotation cadence, zero-downtime handover, secret storage, and detection of stale credentials. Triggers: 'rotate integration user password', 'connected app secret rotation', 'named credential rotation', 'stale service account', 'zero downtime secret rotation'. NOT for end-user password policies.
wire-service-patterns
Use when designing or reviewing Lightning Web Components that use `@wire`, Lightning Data Service, UI API, or the GraphQL wire adapter, especially for reactive parameters, cache behavior, and refresh strategy. Triggers: 'wire service', 'refreshApex', 'reactive parameter', 'getRecord', 'wire vs imperative Apex'. NOT for component communication or generic lifecycle issues when data provisioning is not the main concern.
webhook-inbound-patterns
Use when implementing an inbound webhook receiver in Salesforce: routing via Apex REST and Salesforce Sites, authenticating webhook payloads via HMAC, ensuring idempotent processing, and handling the 5-second response window. NOT for outbound callouts from Salesforce to external systems (use callouts-and-http-integrations), NOT for general Apex REST service design (use apex-rest-services), NOT for platform events as inbound triggers.
flow-email-and-notifications
Use when sending emails, in-app bell notifications, SMS, or Slack messages from Salesforce Flow. Trigger keywords: 'send email action', 'custom notification', 'bell icon', 'Send Custom Notification', 'SMS from flow', 'Slack notification flow'. NOT for designing or managing email templates from Setup (use admin/email-templates-and-alerts), and NOT for Email Alerts defined in workflow rules.
service-metrics-data-model
Use when designing or reporting on Salesforce Service Cloud service metrics — covers the Case, Entitlement, and CaseMilestone object model, MTTR derivation via BusinessHours.diff(), IsViolated semantics, and ElapsedTimeInMins field usage for SLA reporting. Trigger keywords: MTTR, mean time to resolve, case milestone, entitlement SLA, IsViolated, CaseMilestone, first response time, resolution time, service KPI. NOT for CRM Analytics (Tableau CRM / Einstein Analytics) dashboards or Field Service Lightning work order metrics.
service-data-archival
Use this skill when Service Cloud orgs are consuming excessive data or file storage due to Case-related records, or when compliance requirements demand structured retention and deletion of case history. Trigger keywords: EmailMessage bloat, Email-to-Case storage, ContentDocument archival, case attachment cleanup, compliance retention policy, service storage optimization. NOT for generic data archival across non-Service objects — use data-archival-strategies instead. NOT for CPQ, Sales Cloud, or FSL record archival. NOT for purging custom object data unrelated to Cases.
service-cloud-architecture
Use when designing a Service Cloud solution end-to-end: channel strategy (phone, email, chat, messaging, social), routing model (queue-based vs skills-based Omni-Channel), knowledge strategy, entitlement and SLA enforcement, Einstein Bot / Agentforce deflection, and integration points. Triggers: service cloud architecture, case routing design, omni-channel strategy, contact center design, channel strategy, knowledge deflection, service console architecture. NOT for individual feature configuration (use admin/case-management), NOT for Einstein Bot conversation design (use agentforce/einstein-bot-architecture), NOT for telephony CTI implementation details.
multi-channel-service-architecture
Use when designing a unified multi-channel service strategy spanning phone (Service Cloud Voice), email (Email-to-Case), chat (Messaging for In-App/Web), social, and SMS with Omni-Channel routing. Triggers: channel prioritization, unified routing across channels, service channel migration, multi-channel capacity planning. NOT for individual channel setup or configuration — see service-cloud-architecture for single-channel implementation details.
service-cloud-rest-api
Use this skill when integrating with Salesforce Service Cloud REST APIs — specifically the Knowledge REST API (/knowledgeManagement/ for authoring, /support/knowledgeWithSEO/ for guest retrieval) and the Messaging for In-App and Web (MIAW) Enhanced Chat API. Trigger keywords: Knowledge article retrieval, data category filtering, URL-name lookup, Enhanced Chat API, legacy Chat REST API migration. NOT for generic REST API callouts from Apex (use apex__http-callouts), NOT for Salesforce REST API CRUD on standard objects, NOT for Einstein Bot configuration.
fsl-service-report-templates
Use this skill when designing, generating, or troubleshooting Field Service service report templates — covers the createServiceReport REST action (API v40.0+), ServiceReportLayout configuration, DigitalSignature capture, Document Builder (Winter '25+) with conditional logic via Flow, and PDF storage as ContentDocument/ContentVersion. NOT for quote templates, custom Visualforce pages, or Experience Cloud document generation.
apex-rest-services
Use when building, reviewing, or debugging inbound Apex REST resources, request/response handling, status codes, versioned URL mappings, or JSON serialization in `@RestResource` classes. Triggers: 'Apex REST', '@RestResource', 'HttpGet/HttpPost', 'RestContext', 'versioned endpoint'. NOT for outbound HTTP callouts or standard Salesforce REST API usage as a consumer.
apex-outbound-email-patterns
Apex outbound email via Messaging.SingleEmailMessage — OrgWideEmailAddress, ReplyTo and Reply-To header semantics, EmailTemplate merging with whatId/targetObjectId, attachment patterns, daily governor limits, and the difference between transactional sends and Email Alerts. NOT for inbound email handling (use apex/inbound-email-handler) or Marketing Cloud sends (use integration/marketing-cloud-rest-api).