AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

Classic Email Template Migration

skill-pranavnagrecha-awesomesalesforceskills-classic-email-template-migration · by PranavNagrecha

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 /…

No reviews yet
0 installs
28 views
0.0% view→install

Install

$ agentstack add skill-pranavnagrecha-awesomesalesforceskills-classic-email-template-migration

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-pranavnagrecha-awesomesalesforceskills-classic-email-template-migration)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Classic Email Template Migration? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

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 Type | Lightning Successor | Migration Notes | |---|---|---| | Text | Lightning Email Template (text-only mode) | Direct content port; merge fields translate | | HTML with Letterhead | Lightning Email Template + Enhanced Letterhead | Letterhead must be re-built as Enhanced Letterhead first | | Custom HTML | Lightning Email Template (HTML mode, no letterhead) | Paste raw HTML into the Email Template Builder; review for inline-style compatibility | | Visualforce email | NOT migratable — keep as Classic OR rewrite as Lightning Email Template | VF 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 Syntax | Lightning Syntax | Notes | |---|---|---| | {!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 supported | Custom 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 body | Lightning 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:

| Step | Classic Letterhead | Enhanced Letterhead Outcome | |---|---|---| | Header image | Static image referenced by URL | Re-uploaded as Document or referenced via static URL | | Header background color | Hex / named color | Set in Enhanced Letterhead builder | | Body color and fonts | Inline-style settings | Lightning Email Template Builder defaults | | Footer image and text | Static image + text block | Re-built as Enhanced Letterhead footer block | | Sharing | Folder-based | Folder-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.

| Consumer | Where the template ID lives | How to update | |---|---|---| | Email Alert | WorkflowAlert.TemplateId | Edit each alert in Setup; programmatic update via Metadata API | | Flow Send Email action | Flow XML (emailTemplateId) | Edit each Flow; deploy via Metadata API | | Process Builder email action | Process XML | Edit; ideally migrate to Flow first | | Apex Messaging.SingleEmailMessage.setTemplateId() | Code | Find/replace by old → new ID, deploy with the rest of the code | | Approval Process email actions | Approval XML | Edit and deploy | | Marketing tools (Account Engagement, Marketing Cloud) | External system | Update the integration mapping |

5. UiType: The Single Distinguishing Field

EmailTemplate.UiType distinguishes Classic from Lightning:

| UiType | Meaning | |---|---| | Aloha | Classic email template | | SFX | Lightning Email Template (Email Template Builder) | | SFX_Sample | Lightning 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 in Apex).
  2. Retrieve all Email Alerts via Metadata API as XML.
  3. For each ` referencing an old template ID, update the ` 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 )` | Find/replace with new IDs; deploy as code change | Code change is the canonical update mechanism | | OrgWideEmailAddress sender pairing must persist | Re-set the sender on the Email Alert / Apex call | Lightning 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.
  1. 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.
  1. 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.
  1. 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.
  1. 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.
  1. 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.
  1. 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.
  1. 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

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.