EAC Email Filing: Solution Guide
Einstein Activity Capture · Field Guide
Solution guide

EAC Email Filing

Filing Activity Capture emails on the right Opportunity

Version
1.0 (QA-fixed release)
Date
2 November 2026
Org
“agentforce for sales” trial

For business users, Salesforce admins and project stakeholders. Technical detail for developers is in the appendix.

1

At a glance

Activity Capture saves customer emails in Salesforce automatically. EAC Email Filing makes sure each one lands on the deal it is actually about.

The problem

Einstein Activity Capture often files a synced email on the wrong Opportunity. It happens most when one contact works on several deals at once, for example a developer building three Hyatt hotels. The email then shows on the wrong deal's timeline and the right deal's history has a gap.

What it does

Every time a new email is saved on an Opportunity, the solution looks at the contact's open deals, works out which one the email is about, and files it there. When it can't tell, it leaves the email alone and records why, so a person can decide.

128/128
test emails filed correctly (with the agreed decisions)
✓ 0 system errors
114/114
automated tests passing
✓ up from 56
≥91%
code coverage on every part (93% overall)
✓ bar is 75%
21/21
issues found in quality review, all fixed
✓ 0 deferred

Results from 30 October 2026, after the fixes went live in the trial org. “With the agreed decisions” means a few expected answers in the original test pack were changed by the business owner (for example: when the subject and body name different hotels, the body wins). Against the pack exactly as first written, 116 of 128 match.

Key ideas in one minute

  • Rules first, AI last. Most emails (107 of 128 in testing) are settled by simple rules in a fraction of a second. Only unclear emails go to the AI.
  • It never guesses wildly. It only ever chooses among the contact's own open deals, and the AI's answer is used only if it is confident and the email backs it up.
  • Nothing is lost. The email is always saved, even if the filing logic fails.
  • Everything is explained. Each email gets one log entry saying what was decided and why.
  • People stay in charge. If a rep re-files an email by hand later, the solution never overrides it.
2

How it decides

Each new email goes through the same checks, cheapest first. The first check that gives a clear answer wins.

The decision at a glance

Follow an email from the top. Steps 1–6 never use the AI: they are instant rules, and the email record itself is filed. Only an email still unclear after step 6 goes to the AI (steps 7–9), and the AI's pick is used only if it passes every check.

Decision flowchart: when an email is filed by the rules, sent to the AI, or left for a person A new email is saved on an Opportunity 1 Exactly one contact matches the address? No Yes No contact, or two share the address No Contact Match · Ambiguous Contact 2 Does the contact have any open deals? No Yes Nothing to choose from No Open Candidates 3 Only one open deal? Yes No Filed on that deal (no AI) Single Open Deal · Already Correct 4 Email names exactly one of their properties? Yes No Filed on that property (no AI) Formula Match 5 Vague reply in a thread that already has a deal? Yes No Follows the thread's deal (no AI) Thread Memory 6 Names two or more properties? Yes No Left for a person (no AI) Needs Review (Multiple Properties Named) Still unclear: saved where it arrived, then sent to the AI 7 Email contains text aimed at an AI? Yes No Not sent to the AI Needs Review (Instructions In Email) 8 AI says High/Medium and picks an open deal? No Yes Left for a person Needs Review (Prompt Abstained) 9 Does the email back up the AI's pick? No Yes Left for a person Needs Review (Prompt Answer Not In Email) AI files it: the timeline entry moves Auto-Corrected (Prompt Match) Any step: near Salesforce limits, or an error Skipped: Limits · System Error (email still saved) BEFORE SAVE · INSTANT · NO AI AFTER SAVE · 5–16 SECONDS · AI
Green filed automatically Orange left for a person Grey nothing to file Slate the AI step

Each step in detail

Before save Instant. The email itself is filed on the right deal.
1
Who is the customer?
Match the sender's address to a contact (for emails the rep sends, the recipient's).
No Contact MatchAmbiguous Contactif no contact, or two share the address
2
Which deals are open for them?
Only the contact's open Opportunities are ever considered.
No Open Candidatesif they have none
3
Only one open deal?
Then that's the one. No reading, no AI.
Yes → file it thereSingle Open Deal
4
Does the email name exactly one property?
e.g. “Hyatt Studios Winder”. A branded hotel needs its brand too. The body counts more than the subject.
Yes → file it thereFormula Match
5
Is it a vague reply in a known thread?
“Approved, thanks” follows the deal earlier emails in the same conversation went to.
Yes → follow the threadThread Memory
6
Does it name two or more properties?
Too risky to pick one. A person should decide.
Yes → leave for reviewMultiple Properties
↓ still unclear: email is saved where it arrived, then…
After save About 5–16 seconds later, in the background.
7
Ask the AI (Prompt Builder)
Emails that look like they're trying to instruct an AI are not sent at all. The AI's answer is used only if it is High or Medium confidence, picks one of the contact's open deals, and the email backs it up (it mentions the property, its city or its brand, or it is the thread's deal).
Yes → move the timeline entryPrompt Match
8
Otherwise: leave it for a person
The log records the AI's confidence and reasoning to help whoever reviews it.
Needs Reviewthe reason is in the log (see section 3)
Green filed automatically Orange left for a person Grey nothing to file Safety nets at any step: Skipped: Limits System Error (the email is still saved)

Why “before” and “after” matter. Steps 1–6 happen while the email is being saved, so the email record itself shows the right deal. The AI step happens a few seconds later. By then Salesforce no longer lets anyone change which deal an email record belongs to, so the solution moves the email's Activity timeline entry instead. On the deal's timeline it looks the same; the email record keeps its original “Related To”.

What “names a property” means

The email saysWhat happens
“Latest plan for Hyatt Studios Winder”Filed on Winder
“Following up on the Brookhaven site” (deal has no brand)Filed on Brookhaven
“The Winder financing review is complete” (brand missing)Goes to the AI
“Plans for Nashville” (two Nashville deals)Goes to the AI
“Updates on Sugarloaf Mills and Winder?”Left for review
Subject names Sugarloaf Mills, body says “details are for the Brookhaven site”Filed on Brookhaven body wins

A few more rules keep it careful: quoted history in replies (“On Monday, Ana wrote…”) is ignored because it talks about where the thread used to be; accents don't matter (“Queretaro” matches “Querétaro”); a hotel name made of ordinary words, like “Union Station”, only counts when all its words appear; and the brand list (38 Hyatt and partner brands) can be edited by an admin without a release.

3

What each log status means

Every email the solution looks at gets one EAC Match Log entry. Its status tells you what happened and whether you need to do anything.

StatusWhat happenedWhat you should do
Filed automatically
Auto-Corrected (Single Open Deal)The contact has only one open deal and the email arrived somewhere else. Filed on that deal, before save.Nothing.
Auto-Corrected (Formula Match)The email named exactly one of the contact's properties. Filed there, before save.Nothing. (If the reasoning says “decided by the rules after save”, only the timeline entry moved.)
Applied From Thread MemoryA vague reply. Filed on the deal its conversation was already about, before save.Nothing.
Auto-Corrected (Prompt Match)The AI was confident and the email backs up its choice. The timeline entry moved; the email record keeps its original Related To.Nothing. Spot-check now and then.
Already in the right place
Already CorrectThe contact has one open deal and the email is already on it.Nothing.
Confirmed Correct (Formula Match)The email names one property and is already filed on it.Nothing.
Confirmed Correct (Thread Memory)A vague reply, already on its conversation's deal.Nothing.
Left for a person
Needs Review (Multiple Properties Named)The email names two or more properties.Open the email and file it on the right deal if needed.
Needs Review (Prompt Abstained)The AI wasn't confident, or picked something that isn't one of the contact's open deals.Review and re-file if needed.
Needs Review (Prompt Answer Not In Email)The AI was confident, but nothing in the email mentions the deal it picked. Treated as unsafe.Review; the AI's guess is in the reasoning.
Needs Review (Instructions In Email)The email contains text aimed at an AI (a record Id, “ignore the rules”, a “note to the AI”). It was not sent to the AI.Review. If it looks deliberate, tell your admin.
Needs Review (Skipped: Limits)The save was close to Salesforce's processing limits, so filing stopped to make sure the email saved.Review. Many of these: tell your admin.
Ambiguous ContactTwo or more contacts share the address (or an outbound email went to several customers).Review and file by hand.
Nothing to file
No Contact MatchNo contact has this email address.Nothing, unless the sender should be a contact.
No Open CandidatesThe contact has no open deals.Nothing.
Problem
System ErrorSomething failed (for example the AI was unavailable). The email is still saved where it arrived; the reason is in the log.Tell your admin. Review the email.

You may also see a few old entries with Pending Review (Escalate Phase 2). They were written by the previous version (a Flow) between 9 and 15 October 2026 and are no longer produced.

4

Where to see it in Salesforce

The everyday checks, step by step. Start from the App Launcher (the 9-dot grid, top left) or Setup (the gear, top right).

Before you start: ask your admin to give you the EAC Filing Admin permission set. It lets you read the logs and the thread memory. The solution doesn't add a tab for the logs: if App Launcher search finds nothing, add /lightning/o/EAC_Match_Log__c/list?filterName=All after your org's address, or ask your admin to create a tab.

A. See the latest filing decisions

Everyone
App Launcher›EAC Match Logs›List view: All
  1. Open the list view All.
  2. Gear icon (List View Controls) › Select Fields to Display: add Status, Email Message Id, Old What Id, New What Id, Score Confidence, Match Timestamp, Created Date. Save.
  3. Sort by Created Date, newest first.
You should see: the newest decisions at the top, each with a status.

B. Understand one decision

Everyone
EAC Match Logs›open a row
  1. Click the row whose Email Message Id matches the email (the Id is in the email's web address).
  2. Read Status and Reasoning: the rule that applied, or the AI's explanation.
  3. Old What Id is where Activity Capture put the email. New What Id is where it is now (blank if it was left alone).
You should see: exactly one log row per email.

C. Check where an email ended up

Everyone
App Launcher›Contacts›the customer›Activity›the email
  1. Open the email from the contact's Activity tab and read Related To.
  2. Click Related To to open the deal, then its Activity tab: the email should be on the timeline.
  3. Open the other deals for the same contact and check the email is not on them.
You should see: the email on the deal it talks about. If the AI made the decision, the timeline entry is on the right deal even though the email record's Related To still shows where it arrived.

D. See what each conversation remembers

Admins
App Launcher›Email Thread Corrections›List view: All
  1. Show the columns Thread Id, Correct Opportunity Id, Last Modified Date.
You should see: one row per email conversation, pointing at the deal it is currently about. When a conversation moves on to another hotel, its row is updated.

E. Confirm it's switched on correctly

Admins
Setup›Apex Triggers›EmailMessageTrigger
  1. Status should be Active.
Setup›Flows›EAC Auto Correct EmailMessage RelatedTo
  1. This is the previous version. It must not be active (otherwise every email is logged twice).
Setup›Prompt Builder›Opportunity Email Matching
  1. A version should be marked Active (version 6 in the trial org). If none is active, every AI case logs System Error.
Setup›Einstein Setup
  1. Turn on Einstein should be On, and the user that saves synced emails should be allowed to use prompt templates.
Setup›Custom Metadata Types›EAC Brand›Manage Records
  1. 38 brand records (Hyatt Place, Hyatt Studios, Caption by Hyatt, …). Add a record here to teach it a new brand; no release needed.
Setup›Apex Jobs
  1. Filter on the AI job class (EmailPromptFilingQueueable) if AI cases never get a log row.

F. Give someone access to the logs

Admins
Setup›Permission Sets›EAC Filing Admin›Manage Assignments
  1. Click Add Assignment, pick the users who triage email filing, and save.
You should see: they can now open the logs, the thread memory and the email's “EAC Original Related To Id” field (where the email first arrived). It grants read-only access.
5

Testing

The solution was tested by the developer, then independently by a QA review, then re-tested after every fix. On 2 November 2026 a person-run set of 16 hands-on scenarios was added and run end to end; verifying it used 2 AI requests (scenarios 05 and 06).

How it was tested

Developer testing

114 automated tests check each rule, case by case, and run in the org after every release. A pack of 128 realistic sample emails, each with a known right answer, is sent through the real system one at a time and graded automatically.

Independent QA review

A separate QA pass wrote 153 test cases across 15 areas: rules, threads, volume, the AI step, security, error handling and the documentation itself. It found 21 issues (none blocking). All were fixed and re-tested.

Results after the fixes (30 October 2026)

MeasureResult
Sample emails filed correctly128 of 128 with the agreed decisions (116 of 128 as first written)
System errors / emails left without a deal / duplicate logs0 / 0 / 0
Automated tests114 of 114 pass; every part at 91% coverage or more
SpeedRule decisions logged in about 0.3 seconds; AI decisions in about 5–16 seconds
Change versus before the fixesSame filing result for every one of the 128 emails

Who made the decision (128 sample emails)

Decided byEmailsWhere it happens
Property named102Before save
Thread memory5Before save
AI, confident and backed by the email6After save (timeline entry moved)
AI not sure, left for review12After save
Two or more properties, left for review3Before save (AI not asked)

The 15 emails left for review were expected to be: they are genuinely unclear (“Thanks, that works for us” with no earlier email in the thread) or name several hotels. Only two emails (207 and 208) can differ between runs: 207 mentions a second hotel in passing, and the AI sometimes picks either one.

The test plan in five parts

PartWhat it provesTime
1. Pre-flight checksThe right things are installed and switched on10 min
2. Automated testsEach rule behaves as designed5 min
3. 128-email test packA realistic set of emails is filed correctly15 min
4. Live Activity Capture testsIt works on real synced mail without fighting Activity Capture1–2 hrs
5. Safety and rollbackA failure never loses an email, and it can be switched off20 min

Still to do before production: Part 4 in a Hyatt sandbox. Everything so far used synthetic emails in a trial org. Part 4 sends real emails through a mailbox that Activity Capture syncs, and confirms three things the design depends on: Activity Capture files emails on Opportunities; replies keep the same conversation identifier; and Activity Capture doesn't move a corrected email back (checked again after 24 hours).

Hands-on test scenarios

A kit for one person to try every main filing case in the trial org: a set of demo deals, one sample email per case, a script that “delivers” each email, and what to look for afterwards. The script saves the email exactly as an Activity Capture sync would, so the real solution runs. Nothing is faked and nothing is rolled back, and the scripts never change the solution itself.

Your test records

These demo records are already in the org (created 2 November 2026). Every one is tagged EAC-DEMO, so the clean-up never touches anything else. The rep in the sample emails, Jordan (rep@eacdemo-hotels.example.com), is not a contact; he is only the other side of the conversation.

ContactDeals (Opportunities) and why they matterAccount
Dana ReyesFour open deals: Hyatt Place Tampa Westshore, Hyatt Studios Lakeland, Orlando Hotel Development (no brand; its description mentions the Lake Nona campus) and Union Station Hotel Development (no brand, two property words). Plus Hyatt Studios Brandon, which is Closed Lost and never a candidate.Bayside Hospitality Group
Marcus LeeExactly one open deal: Hyatt Place Sarasota Downtown.Bayside Hospitality Group
Priya Shah and Owen ShahShare one email address (a shared desk), so the solution can't tell who wrote.Gulf Coast Partners
Carla NguyenHer only deal, Hyatt Studios Ocala, is Closed Won.Gulf Coast Partners

Both Accounts are named with an “EAC-DEMO” prefix (for example “EAC-DEMO Bayside Hospitality Group”). Deal names have no prefix on purpose, because the rules read every word of a deal name. Record Ids and addresses are in the appendix.

How to run a scenario

1

Run the script

From the project folder, with the Salesforce CLI logged in to the trial org:

sf apex run --file scripts/manual_tests/NN_x.apex

2

Wait

Rule decisions are instant. The AI scenarios (05, 06) and the safety check 07 are logged by a background job: wait about 20 seconds (10 for 07).

3

Check it in Salesforce

Open the newest log row (path A and B in section 4), then the deal's Activity timeline (path C). For scenario 03, also the thread memory (path D).

App Launcher›Opportunities›the deal›Activity›the email by subject›Related To

Unless a card says otherwise, every email arrives on Hyatt Place Tampa Westshore, standing in for Activity Capture's guess, so a correct move shows the email leaving Tampa. Run the scenarios in order; 03a must come before 03b, the others are independent. Each script also prints a one-line result, and check_latest.apex prints the full log row for the newest demo email.

Filed automatically
01

The email names one property

Auto-Corrected (Formula Match)
Arrives onHyatt Place Tampa Westshore→Should end up onHyatt Studios Lakeland
You should see: “Site visit” on Lakeland's timeline with Related To = Lakeland, and gone from Tampa. Log: Old = Tampa, New = Lakeland, Score Confidence 1, filed before save.
02

The email names the deal it arrived on

Confirmed Correct (Formula Match)
Arrives onHyatt Place Tampa Westshore→Should end up onHyatt Place Tampa Westshore (no move)
You should see: the email stays on Tampa; the log confirms the arrival deal was right (Old = New = Tampa).
03

Thread memory: a vague reply follows its conversation

Applied From Thread Memory
Both arrive onHyatt Place Tampa Westshore→Should end up onHyatt Studios Lakeland
You should see: after 03a, a thread memory row (path D) pointing at Lakeland. After 03b, both emails on Lakeland's timeline and the newest log row says Applied From Thread Memory, with no AI request. The “Re:” subject is ignored, so this really tests memory.
05

No deal named; the AI finds it from a landmark

Auto-Corrected (Prompt Match)
Arrives onHyatt Place Tampa Westshore→Should end up onOrlando Hotel Development (timeline entry)
You should see: after about 20 seconds, “Soil report” on Orlando's timeline, while the email's Related To still shows Tampa (expected: after save only the timeline entry can move). The reasoning quotes the Lake Nona sentence. 1 AI request. AI answers can vary: Needs Review (Prompt Abstained), with nothing moved, is an acceptable result.
08

The contact has only one open deal

Auto-Corrected (Single Open Deal)
Arrives onHyatt Place Tampa Westshore (not Marcus's deal)→Should end up onHyatt Place Sarasota Downtown
You should see: the email on Sarasota with Related To = Sarasota, filed before save even though it names nothing. No AI request.
09

Same email, already on the right deal

Already Correct

The same email as 08, from Marcus Lee.

Arrives onHyatt Place Sarasota Downtown→Should end up onHyatt Place Sarasota Downtown (no move)
You should see: a log row saying Already Correct; nothing moves.
13

An email the rep sent: the contact comes from To

Auto-Corrected (Formula Match)
Arrives onHyatt Place Tampa Westshore→Should end up onHyatt Studios Lakeland
You should see: the email on Lakeland, and the log's contact is Dana Reyes, found from the To address because the sender is the rep.
Left for a person
04

The email names two properties

Needs Review (Multiple Properties Named)
Arrives onHyatt Place Tampa Westshore→Should end up onHyatt Place Tampa Westshore (left for review)
You should see: the email still on Tampa; the log's New What Id is blank and Score Confidence is 2. It is not sent to the AI.
06

A vague email with no thread

Needs Review (Prompt Abstained)
Arrives onHyatt Place Tampa Westshore→Should end up onHyatt Place Tampa Westshore (left for review)
You should see: after about 20 seconds, Score Confidence Low, New What Id blank, and reasoning saying the email has no property, brand or city. 1 AI request. Needs Review (Prompt Answer Not In Email) is also a pass; either way nothing moves.
10

Two contacts share the sender's address

Ambiguous Contact
Arrives onHyatt Place Tampa Westshore→Should end up onHyatt Place Tampa Westshore (left for review)
You should see: not filed, even though it names a property, because Priya and Owen Shah both have this address. Reasoning: “Multiple contacts share this email address …”.
Nothing to file
11

The sender isn't a contact

No Contact Match
Arrives onHyatt Place Tampa Westshore→Should end up onHyatt Place Tampa Westshore (no move)
You should see: a log row with New What Id blank; the email is not filed anywhere.
12

The contact's only deal is closed

No Open Candidates
Arrives onHyatt Studios Ocala (Closed Won)→Should end up onHyatt Studios Ocala (no move)
You should see: the email stays on Ocala; the log has Old = Ocala, New blank. Only open deals are ever candidates.
Safety checks
07

The email tries to instruct the AI

Needs Review (Instructions In Email)
Arrives onHyatt Place Tampa Westshore→Should end up onHyatt Place Tampa Westshore (left for review)
You should see: after about 10 seconds, a log row with New What Id and Score Confidence blank. The email is never sent to the AI, so no AI request is used.
15

“Labor union” doesn't name Union Station

Auto-Corrected (Formula Match)
Arrives onHyatt Place Tampa Westshore→Should end up onHyatt Studios Lakeland
You should see: filed on Lakeland with Score Confidence 1. Dana's Union Station deal counts as named only when both “union” and “station” appear, so the single word doesn't make this a two-property email.
14

An email filed on an Account is left alone

No log row
Arrives onEAC-DEMO Bayside Hospitality Group (an Account)→Should end up onthe same Account (no move)
You should see: the email on the Account's Activity tab and no new log row. The solution only handles emails that arrive on an Opportunity.

Results on 2 November 2026

Every scenario was run once in the trial org with the sample emails exactly as above. All passed first time.

#ScenarioExpectedActualResult
01One property namedAuto-Corrected (Formula Match)Same, score 1; on LakelandPass
02Names the deal it arrived onConfirmed Correct (Formula Match)Same, score 2; stayed on TampaPass
03aThread, first emailAuto-Corrected (Formula Match)Same; memory row → LakelandPass
03bThread, vague replyApplied From Thread MemorySame; on LakelandPass
04Two properties namedNeeds Review (Multiple Properties Named)Same, score 2; stayed on TampaPass
05Landmark only (AI)Auto-Corrected (Prompt Match)Same, High; timeline entry on OrlandoPass
06Vague, no thread (AI)Needs Review (Prompt Abstained)Same, Low; stayed on TampaPass
07Instructions aimed at the AINeeds Review (Instructions In Email)Same; stayed on TampaPass
08One open deal, arrived elsewhereAuto-Corrected (Single Open Deal)Same; on SarasotaPass
09One open deal, arrived on itAlready CorrectSame; stayed on SarasotaPass
10Shared addressAmbiguous ContactSame; stayed on TampaPass
11Unknown senderNo Contact MatchSame; stayed on TampaPass
12Only deal closedNo Open CandidatesSame; stayed on OcalaPass
13Sent by the repAuto-Corrected (Formula Match)Same; on LakelandPass
14Filed on an AccountNo log rowNo log row; stayed on the AccountPass
15“Labor union”Auto-Corrected (Formula Match)Same, score 1; on LakelandPass

Testing through the Salesforce screens

What was checked on 2 November 2026, and what wasn't:

QuestionFinding
Is the org set to send system email only?No. The org allows user email (checked without sending anything).
Does sending an email from a deal's Activity › Email composer go through the filing?Not verified. No email was sent from the composer during the check. From the design you would expect it to behave like scenario 13, because a composer email is sent by the rep.
Can a tester start a thread or receive a customer email from the screens?No. The composer has no conversation identifier field, and customer-to-rep emails need Activity Capture or Email-to-Salesforce. Scenario 03 and all inbound scenarios need the scripts (or real Activity Capture, test plan Part 4).

Resetting between runs and cleaning up

Between runs

Run cleanup_demo.apex. It removes the demo emails, their timeline entries, their log rows and the demo thread memory, and keeps the demo Accounts, Contacts and Deals. Then run leftover_check.apex: it should report 0 emails, tasks, logs and thread memory, with 2 Accounts, 5 Contacts and 7 Deals kept. Always clean up before repeating scenario 03, or 03a starts with memory from the last run.

Removing everything

To also delete the demo records, set DELETE_DEMO_RECORDS = true at the top of cleanup_demo.apex, run it, then set it back. seed_demo.apex re-creates them; it is safe to re-run. The clean-up only touches records tagged EAC-DEMO.

6

Quality review & fixes

The QA review found 21 issues: 0 blocking, 5 major, 13 minor and 3 documentation. All 21 were fixed, released on 30 October 2026 and re-tested. The most important ones:

Large batches of long emails could fail to save

Major

When many long emails arrived at once, the filing logic used up Salesforce's processing limits and the whole batch of emails failed to save.

Before200 emails of ~20,000 characters: none saved (memory limit exceeded). 251 emails of ~9,800 characters: 8.9 s of the 10 s limit.
AfterAll 200 save (2.4 s). 251 emails: 3.3 s. It reads only the new part of each email and stops filing (never saving) if limits get close.

HTML-only emails were logged as fixed when they weren't

Major

Many Outlook and Gmail emails have only a formatted (HTML) body. The rules couldn't read it in time, so nothing moved, yet the log said “corrected”.

BeforeEmail and timeline entry stayed on the wrong deal; the log claimed they had moved.
AfterHTML bodies are read before save: the email and its timeline entry are both filed correctly, and the log always matches what really happened.

An email could talk the AI into a filing

Major

QA sent an email containing a hidden “note to the AI” that told it which deal to choose. The AI obeyed.

BeforeFiled on the deal the email asked for, with High confidence.
AfterThree layers: emails like this are not sent to the AI (logged Instructions In Email); the AI is told the email is untrusted; and its answer must be backed by the email. 0 false alarms on the 128 sample emails.

One ordinary word could file an email on the wrong deal

Major

“Our labor union reviewed the terms” was filed on Union Station Hotel Development.

BeforeFiled on Union Station before save, which can't be undone automatically afterwards.
AfterAn unbranded name made of several words needs all of them (“Union Station”). The email above now goes to the AI.

Error handling wasn't tested well enough

Major

One part of the code was below Salesforce's 75% test coverage bar, which could block a production release. The untested lines were the error paths.

Before56 tests; the logging component at 72%.
After114 tests, all passing; that component at 100%, every part at 91% or more.

A contact with only one open deal still went to the AI

Minor · business decision

If the contact has a single open deal, there is nothing to choose. Decided with the business owner on 30 October 2026.

BeforeAn AI request and a few seconds' wait; only the timeline entry could move.
AfterFiled on that deal instantly, before save, with no AI request (Single Open Deal).

Other fixes, in brief

  • Accents no longer matter (“Zoetry” matches the “Zoëtry” brand).
  • Replies in other languages (“AW:”, “SV:”, “Antw:”) are recognised as replies.
  • Two brands on the same site (Hyatt Place and Hyatt House Austin Airport) are told apart by the brand.
  • A reply saved together with the first email of its conversation now follows it.
  • The AI is never given a closed deal as a hint; its answers are read more reliably.
  • A failure in the background AI job now logs an error for every affected email instead of losing them.
  • New EAC Filing Admin permission set, so admins can see where an email first arrived.
  • Test tools can now clean up a single test run; documentation corrected throughout.

Known limits

These are understood and accepted, or outside what this solution can control.

Only the first 5,000 characters are read

The rules read the subject and the first 5,000 characters of the new message (quoted history removed). If the hotel is only named further down, the email goes to the AI instead. Accepted by the business owner; real emails usually name the hotel early. The AI still sees the whole email.

Some influence over the AI remains

A sender can still nudge the AI among deals their email itself mentions, for example by naming a city two deals share. That is the same influence anyone has by naming a hotel, and it is limited to the contact's own open deals.

Other automation can still hit limits

The solution keeps its own processing in check, but other flows or triggers on emails and tasks (including Activity Capture itself) can still push a save over Salesforce's limits.

AI decisions only move the timeline entry

After save, Salesforce won't let an email record change deal. The timeline is right; the email record's Related To still shows where it arrived.

Only emails filed on an Opportunity are handled

If Activity Capture files an email on an Account or Contact, or on nothing, it is skipped with no log entry.

Some emails will always need a person

A lone “Thanks, that works” with no earlier email in the thread, or an outbound email to several customers, is left for review. AI answers can vary on borderline emails.

Short hotel names that are ordinary words

A one-word unbranded name such as “Gateway” still matches on that word alone.

Live mailbox test not yet done

Part 4 of the test plan (real synced mail in a Hyatt sandbox) is the go-live gate and hasn't been run yet.

7

Safety & rollback

Designed so that the worst case is “an email stays where Activity Capture put it”.

The email is always saved

If any part of the filing fails, the email is still saved where it arrived and the problem is logged as System Error.

It only acts on new emails

It looks at each email once, when it's first saved. A rep's later manual re-filing is never overridden.

It stays within the contact's open deals

An email can only be moved to one of that contact's open Opportunities. Closed deals and other contacts' deals are never used.

When unsure, it does nothing

Unclear emails are left alone and logged for review rather than guessed.

How to switch it off

  1. Deactivate the trigger EmailMessageTrigger. In a sandbox: Setup › Apex Triggers › Edit › untick Is Active. In production this is done through a small release that sets it to Inactive.
  2. Optional: bring back the previous behaviour by activating version 8 of the old Flow (EAC Auto Correct EmailMessage RelatedTo), which is kept for this purpose.
  3. Never have both on at once. Every email would be logged twice and the two could disagree.

Turning it off doesn't undo anything already filed; it only stops new emails being filed. Copies of the code from before the QA fixes, and the original Flow, are kept in the project.

Before production

  1. Run the full test plan in a Hyatt sandbox with Activity Capture syncing a real mailbox, especially Part 4.
  2. Release to production and deactivate the old Flow in the same window.
  3. Give the EAC Filing Admin permission set to the admins who triage email filing.
A

Appendix: technical reference

For admins and developers. The full technical write-up is EmailAutoCorrect.md in the project folder.

Components

ComponentTypePurpose
EmailMessageTriggerApex triggerEntry point (before and after insert)
EmailFilingDeciderApex classThe decision rules, shared by every stage
EmailPreSaveFilerApex classBefore save: sets the email's Related To
EmailFilingRecorderApex classAfter save: writes log rows and thread memory, queues AI cases
EmailPromptFilingQueueableApex class (Queueable)Calls Prompt Builder, moves the Task, logs the result (20 emails per job)
EmailFilingLogApex classBuilds and saves log rows; upserts thread memory
EmailFilingTextApex classBounded text the rules read; reply prefixes; flags instruction-like text
EmailOppMatchScorerApex classBrand + property scorer
EmailRecipientSplitterApex classSplits multi-recipient To addresses
EmailPromptOutputParserApex classParses the AI's answer
Opportunity_Email_MatchingPrompt template (v6 active)AI decision for unclear emails
EAC_Brand__mdt (38 records)Custom metadata typeBrand phrase list, editable in Setup
EAC_Match_Log__cCustom objectOne log row per decision
Email_Thread_Correction__cCustom objectThread memory, one row per thread (Thread_Id__c unique)
EmailMessage.EAC_Original_RelatedToId__cCustom fieldWhere the email arrived, when moved before save
EAC_Filing_AdminPermission setRead on the log, thread memory and the field above
EAC_Auto_Correct_EmailMessage_RelatedToFlowPrevious version; kept for rollback, must stay inactive
8 *Test classes, EacTestDataFactoryApex tests114 tests

Settings worth knowing

SettingValueHow to change
Brand list38 recordsSetup › Custom Metadata Types › EAC Brand (no release)
AI confidence acceptedHigh, MediumCode change
Text the rules readSubject ≤ 1,000; body ≤ 5,000 charsCode change (EmailFilingText)
Limit budget before filing stops60% of CPU and heapCode change (EmailFilingDecider)
Emails per background AI job20Code change

Org and deploy history

Org: “agentforce for sales” (00Daj00000tZc9BEAS), an Enterprise Edition trial expiring 27 April 2027. Not production. This org skips Apex tests during deploys, so tests are run straight after each deploy.

DateDeployWhat
15 Oct 20260Afaj00000nrqsKCAQ
0Afaj00000nsRgYCAU
Flow versions 7 and 8 (brand scorer, body-first rule, thread memory fix)
29 Oct 20260Afaj00000ped6wCAAApex version (trigger, classes, new field); Flow deactivated. 56/56 tests
30 Oct 20260Afaj00000ppbucCAA (21 components)QA fixes DEF-001 to DEF-021, EmailFilingText, permission set, prompt template v6 active. 114/114 tests; pack 128/128 with decisions

Version history of results

VersionPack as written (120)With decisions (120)Thread emails (8)Unlinked
Flow v6 (starting point)99105not run13
Flow v710711840
Flow v810812080
Apex, after QA fixes (live)10812080

Hands-on demo records (seeded 2 Nov 2026)

Tagged EAC-DEMO in Account Number, Contact Department and Opportunity Next Step. Open any record by adding /lightning/r/<Id>/view after the org address.

RecordIdDetail
EAC-DEMO Bayside Hospitality Group001aj00004E2hErAAJAccount (Dana, Marcus)
EAC-DEMO Gulf Coast Partners001aj00004E2hEsAAJAccount (Priya, Owen, Carla)
Dana Reyes003aj00002ZGe17AADeacdemo.dana.reyes@example.com
Marcus Lee003aj00002ZGe18AADeacdemo.marcus.lee@example.com
Priya Shah003aj00002ZGe19AADeacdemo.shared.desk@example.com
Owen Shah003aj00002ZGe1AAATeacdemo.shared.desk@example.com
Carla Nguyen003aj00002ZGe1BAATeacdemo.carla.nguyen@example.com
Hyatt Place Tampa Westshore006aj00000gKWunAAGQualification · Dana
Hyatt Studios Lakeland006aj00000gKWuoAAGDiscovery · Dana
Orlando Hotel Development006aj00000gKWupAAGQualification · Dana
Union Station Hotel Development006aj00000gKWuqAAGQualification · Dana
Hyatt Studios Brandon006aj00000gKWurAAGClosed Lost · Dana
Hyatt Place Sarasota Downtown006aj00000gKWusAAGProposal/Quote · Marcus
Hyatt Studios Ocala006aj00000gKWutAAGClosed Won · Carla

File locations (project folder)

PathContents
EmailAutoCorrect.mdFull technical design
TESTING_GUIDE.mdTest plan, Parts 1–5, with commands and pass criteria
qa/QA_REPORT.md, qa/DEFECTS.mdQA findings and the 21 defects
qa/DEV_FIX_REPORT.mdHow each defect was fixed and re-tested
qa/CLICK_PATHS.md, qa/TEST_CASES.mdManual click paths; 153-case test matrix
force-app/main/default/Salesforce metadata (classes, trigger, objects, template, permission set)
scripts/eac_test/Test pack harness (run_eac_test.py) and offline scorer
qa/MANUAL_TEST_GUIDE.mdHands-on scenario guide (16 runs, verified 2 Nov 2026), with record links and pass/fail criteria
scripts/manual_tests/01_*.apex … 15_*.apex (one sample email each; variables at the top), seed_demo.apex, check_latest.apex, cleanup_demo.apex, leftover_check.apex
test-data/Synthetic Opportunities, contacts, the 128 sample emails, results; demo_ids.json (hands-on demo record Ids)
.backup/Code and docs as they were before the QA fixes