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

$curl -o ~/.claude/skills/email-service-inbound/SKILL.md --create-dirs "https://raw.githubusercontent.com/PranavNagrecha/AwesomeSalesforceSkills/main/skills/admin/email-service-inbound/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/email-service-inbound/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How email-service-inbound Compares

Feature / Agentemail-service-inboundStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/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

8
from PranavNagrecha/AwesomeSalesforceSkills

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

8
from PranavNagrecha/AwesomeSalesforceSkills

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

8
from PranavNagrecha/AwesomeSalesforceSkills

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

8
from PranavNagrecha/AwesomeSalesforceSkills

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

8
from PranavNagrecha/AwesomeSalesforceSkills

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

8
from PranavNagrecha/AwesomeSalesforceSkills

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

8
from PranavNagrecha/AwesomeSalesforceSkills

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

8
from PranavNagrecha/AwesomeSalesforceSkills

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

8
from PranavNagrecha/AwesomeSalesforceSkills

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

8
from PranavNagrecha/AwesomeSalesforceSkills

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

8
from PranavNagrecha/AwesomeSalesforceSkills

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

8
from PranavNagrecha/AwesomeSalesforceSkills

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).