本文へ移動
cccskills
無料GitHub で公開

classic-email-template-migration

Migrating Classic email templates (Text, HTML with Letterhead, Custom HTML, Visualforce email) to Lightning Email Templates (LET) and the Email Template Builder. Covers merge-field translation, Letterhead-to-Enhanced-Letterhead conversion, Visualforce email retention strategy, folder reorganization, sender-context (OrgWideEmailAddress) preservation, and downstream Email Alert / Process Builder / Flow rewiring. NOT for transactional Apex email sending (use apex/apex-outbound-email-patterns) or marketing email broadcasts (use Marketing Cloud / Account Engagement).

インストール方法を見る

含まれるファイル(7)

  • SKILL.md20.7 KB
  • references/examples.md7.3 KB
  • references/gotchas.md7.2 KB
  • references/llm-anti-patterns.md7.0 KB
  • references/well-architected.md6.8 KB
  • scripts/check_classic_email_template_migration.py4.8 KB
  • templates/classic-email-template-migration-template.md2.9 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Classic Email Template Migration

This skill activates when a practitioner needs to convert Classic email templates (Text, HTML with Letterhead, Custom HTML, Visualforce email) to Lightning Email Templates and rewire all downstream consumers.


Before Starting

Gather this context before working on anything in this domain:

  • Inventory templates by type. SELECT Id, Name, TemplateType, FolderId, IsActive FROM EmailTemplate WHERE UiType = 'Aloha' returns Classic templates. TemplateType is one of text, html (with Letterhead), custom (raw HTML), visualforce.
  • Identify the downstream consumers per template: Email Alerts (SELECT Id, FullName FROM WorkflowAlert WHERE TemplateId = :t), Flow Send Email actions, Process Builder email actions, Apex code calling Messaging.SingleEmailMessage.setTemplateId(t), and any Marketing/CRM campaign that references the template.
  • Confirm the org's brand strategy: do you have an Enhanced Letterhead already, or will the migration create one from the existing Classic Letterhead images? Letterhead colors, logos, and footers must survive the migration intact.
  • Determine OrgWideEmailAddress usage. Templates often pair with a specific sender ("noreply@", "sales@"). Lightning Email Templates do not store sender — that's set at send time — so the migration must preserve the pairing in downstream Email Alerts and Apex.

Core Concepts

1. Template Type Mapping

Classic Template TypeLightning SuccessorMigration Notes
TextLightning Email Template (text-only mode)Direct content port; merge fields translate
HTML with LetterheadLightning Email Template + Enhanced LetterheadLetterhead must be re-built as Enhanced Letterhead first
Custom HTMLLightning Email Template (HTML mode, no letterhead)Paste raw HTML into the Email Template Builder; review for inline-style compatibility
Visualforce emailNOT migratable — keep as Classic OR rewrite as Lightning Email TemplateVF templates support full Apex logic; Lightning has no equivalent server-render

Visualforce email templates are the only type that cannot be converted automatically. Their power (full Apex controllers, conditional logic, related-list iteration) has no Lightning equivalent. The migration decision is per-template: keep it as Classic (still supported), or rewrite the required logic in Lightning + an Apex helper that builds the body string and uses setHtmlBody() instead of setTemplateId().

2. Merge Field Syntax Translation

Classic SyntaxLightning SyntaxNotes
{!Account.Name}{{Recipient.Account.Name}} (when Recipient is the merge object) OR {{Sender.Account.Name}}Lightning explicitly scopes to Recipient / Sender / Related Object
{!Contact.FirstName}{{Recipient.FirstName}} (if recipient IS the Contact)Lightning prefers recipient-relative paths
{!Account.Owner.FirstName}{{Recipient.Account.Owner.FirstName}}Cross-object spans work the same; just prefix with Recipient/Sender
{!System.URLENCODE($Setup.MyCustomSetting__c.URL__c)}NOT supportedCustom setting and System merge fields don't translate; move to a related-record field if needed
{!IF(Contact.Email != null, Contact.Email, 'no-email@example.com')}NOT supported in template bodyLightning templates don't support IF/CASE merge formulas; pre-compute on a record formula field
{!Account.LastModifiedDate} formatted{{Recipient.Account.LastModifiedDate}}Date formatting in Lightning is via the recipient's locale; no inline format spec

3. Letterhead vs Enhanced Letterhead

Classic Letterhead and Enhanced Letterhead are two different Letterhead and EnhancedLetterhead sObjects with no auto-conversion path. Migration requires:

StepClassic LetterheadEnhanced Letterhead Outcome
Header imageStatic image referenced by URLRe-uploaded as Document or referenced via static URL
Header background colorHex / named colorSet in Enhanced Letterhead builder
Body color and fontsInline-style settingsLightning Email Template Builder defaults
Footer image and textStatic image + text blockRe-built as Enhanced Letterhead footer block
SharingFolder-basedFolder-based (EnhancedLetterhead.FolderId)

You build the Enhanced Letterhead once per brand; many Lightning Email Templates can reference the same Enhanced Letterhead.

4. Downstream Consumer Rewiring

Every reference to a Classic template ID must be updated. Lightning template IDs are different IDs, so blanket text substitution is not enough — you need a mapping table.

ConsumerWhere the template ID livesHow to update
Email AlertWorkflowAlert.TemplateIdEdit each alert in Setup; programmatic update via Metadata API
Flow Send Email actionFlow XML (emailTemplateId)Edit each Flow; deploy via Metadata API
Process Builder email actionProcess XMLEdit; ideally migrate to Flow first
Apex Messaging.SingleEmailMessage.setTemplateId()CodeFind/replace by old → new ID, deploy with the rest of the code
Approval Process email actionsApproval XMLEdit and deploy
Marketing tools (Account Engagement, Marketing Cloud)External systemUpdate the integration mapping

5. UiType: The Single Distinguishing Field

EmailTemplate.UiType distinguishes Classic from Lightning:

UiTypeMeaning
AlohaClassic email template
SFXLightning Email Template (Email Template Builder)
SFX_SampleLightning sample template (read-only system templates)

Filter queries by UiType to operate on the right cohort. A WHERE UiType = 'Aloha' query is your migration source set; WHERE UiType = 'SFX' is the destination set.


Common Patterns

Pattern 1: Direct Conversion of Custom HTML Template

When to use: Classic Custom HTML templates without Visualforce or merge formulas.

How it works:

  1. Open the Classic template; copy the raw HTML body.
  2. In Setup → Email Template Builder, create a new Lightning Email Template; choose the "HTML" type with no letterhead (or with the new Enhanced Letterhead if applicable).
  3. Switch the builder to source-code view; paste the HTML.
  4. Replace merge fields per the syntax table (Section 2).
  5. Send a test email to a sandbox user; verify rendering across major clients (Outlook, Gmail, mobile).
  6. Save and capture the new template ID for downstream rewiring.

Why not the alternative: The Lightning Email Template Builder's WYSIWYG editor cannot import a Classic template directly — it can only build from blocks or accept HTML source. Trying to recreate the layout in the visual editor is slower and lossy compared to pasting the original HTML.

Pattern 2: HTML-with-Letterhead Template + Enhanced Letterhead Build-Out

When to use: Classic templates that share a single Letterhead (typical: one corporate Letterhead + 30 templates that reference it).

How it works:

  1. Build ONE Enhanced Letterhead first. In Setup → Enhanced Letterheads, recreate the brand (header image, colors, footer).
  2. Verify rendering by attaching the Enhanced Letterhead to a single test Lightning Email Template.
  3. For each Classic template that referenced the Classic Letterhead, create a new Lightning Email Template that uses the Enhanced Letterhead.
  4. Copy the body HTML from the Classic template into the Lightning template body block.
  5. Translate merge fields.
  6. Map each old → new template ID for downstream rewiring.

Why not the alternative: Building Enhanced Letterheads per template scatters brand maintenance and makes future brand updates painful. One Enhanced Letterhead serving N templates is the right architecture.

Pattern 3: Visualforce Email Template — Decide Per Template

When to use: Classic templates with TemplateType='visualforce' that contain Apex controller logic (related-list iteration, conditional content, complex formatting).

How it works:

  1. For each VF template, audit what the controller does. Categorize: (a) simple merge fields (no real Apex needed), (b) related-list rendering, (c) genuine business logic.
  2. Category (a): Re-create as Lightning Email Template with merge fields. Discard the VF template.
  3. Category (b): Decide if Lightning Email Template's {{#each}} over a related list is sufficient (it can iterate, but not all relationships); if not, keep VF or move logic to an Apex helper that builds the body string.
  4. Category (c): Keep as Classic Visualforce email template. Document the retention reason. Salesforce continues to support these.

Why not the alternative: Forcing a category (c) template into Lightning by hand-coding the logic in Apex setHtmlBody() is technically possible but loses the template-builder maintenance UX (admins can't tweak the email; only developers can). Keep VF when the logic justifies it.

Pattern 4: Bulk Email Alert Repointing

When to use: After templates are migrated, dozens or hundreds of Email Alerts still point at Classic template IDs.

How it works:

  1. Build the old → new template ID mapping table (CSV or Map<Id, Id> in Apex).
  2. Retrieve all Email Alerts via Metadata API as XML.
  3. For each <workflowAlert> referencing an old template ID, update the <template> element to the new template's full name.
  4. Deploy the updated metadata in a single change set or DevOps Center release.
  5. Verify via SOQL: SELECT Id, FullName, TemplateId FROM WorkflowAlert WHERE TemplateId IN :oldIds should return zero after deploy.

Why not the alternative: Editing each Email Alert in the Setup UI is feasible at <10 alerts; at scale it is error-prone and slow. Metadata API is the right tool above ~10 alerts.


Decision Guidance

SituationRecommended ApproachReason
Classic Text templateDirect re-creation as Lightning text templateLowest-effort migration; merge fields translate cleanly
Classic HTML w/ Letterhead, single brandBuild one Enhanced Letterhead + re-create templatesBrand maintenance stays centralized
Classic Custom HTMLPaste HTML into Lightning Email Template source viewFaster than rebuilding in WYSIWYG
Visualforce template, simple merge fields onlyRe-create as Lightning template; discard VFSimpler maintenance; admin-editable
Visualforce template, related-list iterationTry Lightning template iteration first; keep VF if Lightning can't model the relationshipLightning iteration is limited to direct related lists
Visualforce template, complex Apex logicKeep as Classic VF templateLightning has no controller equivalent
100+ Classic templates, all use one LetterheadBulk Enhanced Letterhead + scripted template creationManual UI re-creation doesn't scale
Email Alerts reference Classic templatesMetadata API bulk update with mapping tableEditing in Setup is per-alert and slow
Apex code uses setTemplateId(<Classic ID>)Find/replace with new IDs; deploy as code changeCode change is the canonical update mechanism
OrgWideEmailAddress sender pairing must persistRe-set the sender on the Email Alert / Apex callLightning templates don't store sender

Recommended Workflow

Step-by-step instructions for an AI agent or practitioner working on this task:

  1. Inventory and categorize. Run the WHERE UiType='Aloha' SOQL. Group by TemplateType. Generate a CSV with columns: ID, Name, Type, Folder, downstream consumers (count of Email Alerts, Flows, Apex references). This drives the migration plan.
  2. Build Enhanced Letterhead(s). For each unique brand layout in Classic Letterheads, create an Enhanced Letterhead. Keep the count low (1–3 per org); resist per-department variants that fragment brand.
  3. Migrate templates by type. Start with Text and Custom HTML (lowest risk, highest volume) — paste body, translate merge fields, send test, capture new ID. Then HTML-with-Letterhead per Pattern 2: each new Lightning template references the Enhanced Letterhead; body content ports directly.
  4. Triage Visualforce templates. Categorize each per Pattern 3. Most simple ones become Lightning; complex ones stay as VF with a documented retention rationale.
  5. Build the old → new ID mapping table. Persist as a custom object (Email_Template_Migration_Map__c) for ongoing reference and audit.
  6. Rewire downstream consumers. Email Alerts via Metadata API (Pattern 4), Flows via Flow XML edit, Apex via code change. Verify zero references to old template IDs remain.
  7. Verify and deactivate Classic templates. Send test emails through every Email Alert and Flow path. Once confirmed, set Classic template IsActive=false (don't delete — keep as audit trail). Schedule deletion after a soak window if storage is a concern.

Review Checklist

Run through these before marking work in this area complete:

  • Every Classic template has either a Lightning equivalent OR a documented "keep as VF" retention reason
  • Enhanced Letterhead(s) match the original brand (header image, colors, footer); rendered tested in Outlook, Gmail, Apple Mail
  • Merge fields translated to {{Recipient.X}} / {{Sender.X}} / {{Related.X}} syntax
  • No {!IF(...)}, {!CASE(...)}, or {!System.X} merge formulas remain (Lightning doesn't support them)
  • Old → new template ID mapping table is committed and accessible to ongoing operations
  • All Email Alerts updated via Metadata API; verification SOQL returns zero references to old template IDs
  • All Flow Send Email actions updated to new template IDs
  • All Apex setTemplateId() calls updated and deployed
  • OrgWideEmailAddress sender pairings preserved on each downstream consumer
  • Test email path validated end-to-end for at least one alert per template
  • Classic templates set to IsActive=false after verification (not deleted yet)

Salesforce-Specific Gotchas

Non-obvious platform behaviors that cause real production problems:

  1. Lightning Email Templates have no IF/CASE merge formula support. Classic templates often used {!IF(Contact.Email != null, Contact.Email, 'fallback')} for inline conditional content. Lightning templates have no equivalent — the merge engine is straight field interpolation. Migrate the conditional logic upstream: create a formula field on the record (Best_Contact_Email__c = IF(Email != null, Email, 'fallback')) and merge that field instead.

  2. Custom Setting and $Setup merge fields don't translate. Classic templates could reference {!$Setup.MyCustomSetting__c.URL__c}. Lightning has no equivalent merge namespace for custom settings. The fix is to surface the value via a record field (formula or text) on whatever record is being merged.

  3. Visualforce email templates remain functional but invisible in some Lightning UIs. When you select a template in the Lightning Send Email composer, only Lightning templates appear in the picker by default. Visualforce templates show only when explicitly enabled in the user's Email Settings or via the "Show Classic Templates" toggle. Users may report "the template is missing" when it actually exists but is hidden.

  4. UiType is a separate field from TemplateType. Classic and Lightning templates can both have TemplateType='html'. The distinguishing field is UiType — Classic is 'Aloha', Lightning is 'SFX'. Migration scripts that filter only by TemplateType will mix Classic and Lightning templates and produce wrong results.

  5. Folder permissions don't auto-port. Classic templates lived in Email Template Folders with their own sharing model. Lightning templates also use folders, but a Lightning Email Template Folder is a separate Folder object record. Replicating the folder structure and folder-level sharing is a manual step in the migration; otherwise users may not see migrated templates they had access to in Classic.

  6. Letterhead images served from old URLs may be cached or moved. Classic Letterhead headers/footers were often hosted as Documents or external URLs. After migration to Enhanced Letterhead, if the image URL changed (e.g., new Document ID), some recipients' email clients may have cached the old URL with broken-link indicators. Test rendering on real email clients, not just the Salesforce preview.

  7. Email Alert setTemplateId requires the template to be in a folder the running user has access to. Even when an Email Alert references the new Lightning template by ID, send-time access is checked against the user firing the alert. Lightning Email Template folders need explicit sharing to the running profiles or permission sets, or alerts will fail with INVALID_FIELD: TemplateId is invalid.

  8. OrgWideEmailAddress is set on the Email Alert / Apex call, not the template. Classic templates were sometimes paired with a specific from-address via the template setup screen. Lightning templates have no sender field. Re-setting the OrgWideEmailAddress on the downstream Email Alert (or in Messaging.SingleEmailMessage.setOrgWideEmailAddressId() in Apex) is part of the migration — it's not preserved automatically because it was never on the template object.


Output Artifacts

ArtifactDescription
Lightning Email Template recordsEmailTemplate rows with UiType='SFX' replacing each Classic template
Enhanced Letterhead record(s)One per brand; referenced by all branded Lightning templates
Email_Template_Migration_Map__c recordsOld Classic ID → new Lightning ID, plus migration date and outcome
Updated Email Alerts (Metadata API package)WorkflowAlert.template references switched to new template fullnames
Updated FlowsSend Email actions repointed to new template IDs
Updated ApexsetTemplateId calls switched to new IDs
Visualforce template retention listDocumented set of VF templates kept with rationale
Test plan resultsPer-alert send test confirming end-to-end rendering and recipient delivery

Related Skills

  • admin/email-templates-and-alerts — Use when designing new Lightning templates and Email Alerts (post-migration)
  • apex/apex-outbound-email-patterns — Use when Apex code sends transactional email; affected by template ID changes
  • flow/flow-email-and-notifications — Use when Flow's Send Email actions need to be updated to new template IDs
  • admin/email-deliverability-strategy — Use to verify post-migration deliverability and bounce-rate baselines remain stable
  • lwc/visualforce-to-lwc-migration — Use when retained Visualforce email templates also have associated VF UI pages being migrated

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Use this skill when writing test-first, behavior-driven acceptance criteria in Given/When/Then format for a Salesforce user story. Covers happy path, edge cases, negative paths, permission boundaries, and data-state preconditions so the AC block can drive UAT scripts and Apex test design downstream. Trigger keywords: given when then, gherkin, behavior driven AC, test first acceptance criteria, scenario outline, BDD acceptance criteria. NOT for the user-story format itself (use admin/user-story-writing-for-salesforce). NOT for UAT script writing (use admin/uat-test-case-design). NOT for Apex test method generation (use agents/test-class-generator). NOT for high-level UAT planning (use admin/uat-and-acceptance-criteria). More triggers: acceptance criteria record, ac_id, req_id on a criterion, artefact under test, negative case for a rule-type requirement, no-match fall-through criterion, oracle for a Then clause, test type apex flow manual, criteria linter, check_ac_format.py, --manifest-dir.

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

Task and Event objects: polymorphic WhatId/WhoId, Activity object model, ActivityHistory vs OpenActivity, activity timeline customization, bulk task creation, Einstein Activity Capture boundaries. NOT for turning on EAC or calendar sync — use admin/einstein-activity-capture-setup. NOT for Email-to-Case — use admin/case-management-setup. Covers ActivitiesSettings metadata, enableActivities, shared Task/Event custom fields and FLS, TaskStatus value sets, Shared Activities and TaskRelation/EventRelation, recurring and child activities, and partial-success bulk DML on Task.

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

Design Invocable Apex actions that return deterministic, agent-friendly errors instead of surfacing raw exceptions to the LLM. NOT for authoring the action itself — @InvocableMethod schema, labels, security context — use agentforce/custom-agent-actions-apex. NOT for the Apex tests that force each error branch — use agentforce/agent-action-unit-tests.

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

Use when designing how an Agentforce agent extracts structured input parameters (slots) from a user's natural-language utterance to invoke an action. Triggers: 'agent extract date from user', 'agent action argument extraction', 'agentforce slot filling', 'agent invocable input mapping', 'agent fails to fill required parameter'. NOT for action authoring (use agentforce/agent-actions) or for prompt-template variable binding (use agentforce/prompt-builder-templates).

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

Apex test patterns for @InvocableMethod agent actions: per-reason-code branch coverage, bulk safety, callout mocking, deterministic assertions. NOT for testing whether the agent routes to the right topic or answers well — use agentforce/agent-testing-and-evaluation. NOT for designing the error envelope the tests assert on — use agentforce/agent-action-error-handling.

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

Designing or reviewing Agentforce actions: Flow actions, Apex invocable actions, prompt-template actions, action naming, input and output contracts, confirmation requirements, and safe error behavior. Triggers: 'agent actions', 'build an agent that can look up an order', 'make the agent able to check order status', 'give the agent a capability', 'agent that can retrieve a record', 'flow action for agent', 'agent invocable action', 'action schema design'. NOT for writing the Apex class itself — use agentforce/custom-agent-actions-apex. NOT for the reason_code error envelope that stops an agent retry loop — use agentforce/agent-action-error-handling.

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

PranavNagrecha のスキルをすべて見る

このスキルの問題を報告する