Salesforce Einstein Activity Capture: Microsoft Graph Migration Guide (EWS Retires Oct 2026)
EWS soft deadline passed August 3. October 1 is the partial cutoff. Exact steps to migrate Einstein Activity Capture to Microsoft Graph — plus a full custom integration guide for orgs going beyond EAC.
TLDR: If your Salesforce org uses Einstein Activity Capture (EAC) to sync emails and calendar events with Microsoft 365, you need to migrate from Exchange Web Services (EWS) to the Microsoft Graph API. The soft deadline was August 3 — it passed. Microsoft will partially block EWS on October 1, 2026, and fully remove it on April 1, 2027. You still have time before the October cutoff breaks your email sync. This guide covers two tracks: the fast admin upgrade path (switching EAC from EWS to Graph, typically 1–2 weeks) and a full custom Microsoft Graph integration for orgs that want to move beyond EAC’s limitations entirely.
Why This Is Urgent Right Now
Here’s the timeline you need to know:
| Date | What Happens |
|---|---|
| August 3, 2026 | Salesforce soft deadline — recommended migration completion date (passed) |
| October 1, 2026 | Microsoft begins partial EWS block for Exchange Online |
| April 1, 2027 | Microsoft permanently removes EWS from Exchange Online — hard retirement |
If your Salesforce admin didn’t catch the August 3 notice, you’re not alone. The communication from Salesforce on this was easy to miss — buried in release notes and trust alerts rather than direct admin notifications. What matters now is whether you act before October 1.
October 1 is when things actually start breaking. Microsoft’s “partial block” means EWS API requests will begin failing for some tenants — Salesforce won’t be able to fetch emails or push calendar updates through EWS anymore. The result: your sales reps stop seeing logged emails in Salesforce activities, and calendar sync silently dies. April 1, 2027 is the full removal — at that point, any org still on EWS loses sync entirely.
For most orgs, the fix is a straightforward EAC upgrade — 1–2 weeks if your Microsoft 365 admin is responsive. But if EAC’s limitations (no permanent storage, no native reporting, no Teams support) have been a persistent pain point, this migration window is also a natural inflection point to evaluate whether to replace EAC with a direct Microsoft Graph integration. The how-to guide below covers that path in full, with step-by-step instructions, Apex code, and architecture options for orgs of all sizes.
Am I Affected? Check This First
Not every Salesforce org running EAC is on EWS. Orgs provisioned from Spring ‘26 onward were set up on Microsoft Graph by default. Here’s how to check:
- Go to Setup → search for Einstein Activity Capture → open Settings
- Under the Microsoft Exchange configuration section, look for a label showing Exchange Web Services or Microsoft Graph
- If it says Microsoft Graph, you’re already migrated — no action needed
- If it says Exchange Web Services, follow Path A below
- If you’re on Lightning Sync (not EAC at all), follow Path B
You can also check via SOQL: query the ConnectedApplication or ExchangeSyncConfig objects to see which auth method is configured. But the Setup UI check above is faster for most orgs.
Tip: Before you start the migration, run this audit: pull the list of all Salesforce users with active EAC connections (Setup → Einstein Activity Capture → Connected Accounts). Note how many users have personal OAuth connections versus org-level service account connections. The reconnection burden after migration is proportional to that number — and it affects your rollout timeline.
Path A: You’re on EAC (EWS) — Upgrade to Microsoft Graph
This is the most common scenario for orgs set up between roughly 2019 and 2025. You already have EAC running, but it’s using the older EWS protocol. The upgrade keeps EAC in place and just switches the underlying auth and API layer.
Step 1: Get Your Microsoft 365 Admin Involved First
Don’t skip this step, and don’t treat it as a five-minute task. Microsoft Graph requires tenant-wide admin consent in Microsoft Entra ID (formerly Azure Active Directory). Your Microsoft 365 global admin needs to grant the Salesforce app the necessary Graph API permissions.
The Salesforce app requires these Graph API permissions: Calendars.ReadWrite, Contacts.ReadWrite, Mail.Read, Mail.ReadWrite, offline_access, openid, profile, User.Read.
What this means in practice: if your Microsoft 365 admin is a different team (or a different company, for shared-tenant setups), you need to open that ticket now. Waiting on Entra admin consent is the most common reason migrations slip past deadlines. Budget at least 3–5 business days for back-and-forth, more if you’re in a large enterprise with a change management process.
Step 2: Initiate the Upgrade in Salesforce Setup
Once admin consent is granted in Entra:
- Go to Setup → Einstein Activity Capture → Settings
- Click Upgrade to Microsoft Graph
- Follow the OAuth consent flow for the Salesforce-to-Microsoft connection at the org level
- Complete the Microsoft login as a user with Exchange admin permissions
This step reconfigures EAC’s outbound authentication from EWS basic/OAuth to Graph API tokens. It doesn’t change your EAC configuration settings (sync directions, which objects sync, field mappings) — those carry over.
Step 3: User Reconnection
If your org uses per-user OAuth connections (the majority of EAC setups), each user will need to reconnect their Microsoft account after the migration. You can’t automate this part — it requires user action.
Options for communicating this:
- Send a Salesforce Chatter broadcast to all EAC-connected users with a reconnection link and instructions
- Use a Salesforce Flow to create a homepage banner prompting users to reconnect
- Coordinate with your Salesforce admin to monitor Connected Accounts and chase outstanding reconnections
Users who don’t reconnect won’t lose historical activity data — but going forward, their emails and calendar events won’t sync to Salesforce until they reconnect.
Step 4: Validate Sync Health
After the migration:
- Go to Setup → Einstein Activity Capture → Sync Health
- Check for any sync errors — look specifically for auth failures or permission errors that would indicate the Entra consent step didn’t cover all required scopes
- Test by logging a new email from Outlook and confirming it appears as an activity in Salesforce within 30 minutes
- Test calendar sync: create a meeting in Outlook for a Salesforce-connected contact and confirm it appears in Salesforce
Earned insight: In post-migration validations, the most common early failure isn’t the auth setup — it’s that the Microsoft 365 admin granted consent on a non-production tenant first, or granted consent as a user without global admin rights. Entra’s consent UI doesn’t always surface this clearly. If sync health shows auth errors immediately after migration, the first thing to check is whether admin consent was granted by an actual global admin on the correct tenant.
Path B: You’re Still on Lightning Sync — Migrate to EAC First, Then Upgrade
Lightning Sync is an older Salesforce sync product that predates EAC. If you’re still on LS, you have two problems: LS is itself deprecated, and even if you migrated LS from EWS, you’d just be extending the life of a product with no future. The right move is to migrate to EAC (and configure it on Microsoft Graph from the start) in one step.
Audit Your Current Lightning Sync Configuration
Before migrating, document what LS is currently doing:
- Which users are connected? (Setup → Lightning Sync → Users)
- Sync directions: contacts only? Contacts + calendar? Bidirectional?
- Any custom field mappings beyond defaults?
- Any sync filters (e.g., only sync contacts owned by the user)?
Critical note: EAC does NOT inherit Lightning Sync settings. You’re not upgrading LS — you’re replacing it. Every configuration decision needs to be remade in EAC. Documenting current LS config before you start is the only way to avoid missing something.
Check Your EAC License Availability
EAC is included at no additional cost with Sales Cloud and Service Cloud (100 free seats per edition). Most orgs that are still on LS are in this situation because they never activated EAC, not because they can’t access it.
Go to Setup → Company Settings → Company Information → check the Feature Licenses section for Einstein Activity Capture User licenses. If you have them, you’re ready. If not, contact your Salesforce AE.
Run the EAC Migration Assistant
Salesforce provides a Migration Assistant in Setup:
- Go to Setup → Einstein Activity Capture → Settings → Migrate from Lightning Sync
- The assistant will walk you through creating a new EAC configuration, selecting the users to migrate, and mapping sync preferences
- During setup, you’ll connect to Microsoft Graph directly (not EWS) — this is the right moment to use Graph, not EWS, since EWS is the thing you’re escaping
- The assistant will then disable LS for migrated users and activate EAC
After the LS migration completes, follow Path A Steps 3–4 above to handle user reconnection and validate sync health.
Gotchas That Hit Organizations Mid-Migration
Activity 360 Reporting Dependencies
Activity 360 Reporting, Activity Metrics, and the Activities Dashboard are being retired in Summer ‘26. If your org uses these features — common in sales ops teams that report on email activity volume by rep — audit those dependencies before migrating. Post-migration, this reporting will need to shift to CRM Analytics or custom reporting on the Activity object. Don’t complete the migration without knowing who relies on these reports and having a replacement plan.
Shared Calendar Sync Doesn’t Work in EAC
EAC only syncs a user’s own calendar — it doesn’t support shared calendars or resource calendars (conference rooms, shared team calendars). If your Lightning Sync setup included any shared calendar syncing, that functionality goes away with EAC and doesn’t come back with Microsoft Graph migration. You’ll need a third-party connector (Boomi, custom middleware, or AppExchange calendar tools) if shared calendar sync is business-critical.
Automation on Task, Event, EmailMessage Objects
If your org has Flows, Apex triggers, or process builders that fire on Task, Event, or EmailMessage creation — and that includes any automation triggered by synced activities — test those against EAC-synced records in a sandbox before going to production. EAC creates activities in Salesforce differently than manual entry or the old Lightning Sync, and field population behavior (particularly ActivityDate, WhoId, WhatId) may not match what your automation expects.
Warning: Don’t migrate to Microsoft Graph in production over a quarter close. EAC sync health issues during a migration — even temporary ones — can cause emails to go unlogged and pipeline activity to appear to drop. Time your migration for the first week of a new quarter, not the last two weeks.
Phased Rollout Is Safer Than Big-Bang
If you have more than 100 users connected to EAC or LS, do the Microsoft Graph migration in waves:
- Pilot group: 10–20 technically proficient sales reps who can report issues clearly (not your most revenue-critical reps)
- Monitor sync health for 5 business days
- Broad rollout: remaining users in batches of 50–100 per week
- Monitor connected accounts daily until all users have reconnected
This is more coordination work, but it turns a potential 500-user sync outage into a 20-user recoverable pilot.
What Happens If You Do Nothing
If you miss the October 1, 2026 deadline:
- Microsoft begins blocking EWS requests from Exchange Online — your email sync breaks for some or all users, depending on Microsoft’s rollout pace
- Salesforce activities for emails stop logging — reps lose visibility into contact history in Salesforce
- Calendar sync stops working — meetings with Salesforce contacts don’t appear in Salesforce, and Salesforce-created events may not push to Outlook
- No historical data is lost (it’s already in Salesforce) but you stop capturing new activity
If you miss April 1, 2027:
- EWS is fully removed — complete sync failure for any org still on EWS or LS. No partial functionality.
The fix after the deadline is the same as the fix before it. But support queue wait times from Salesforce and Microsoft both spike around deprecation deadlines. Migration before October 1 is a smoother experience than migration under broken-sync pressure in Q4.
30-Day Action Checklist
Here’s a scannable checklist you can use to track progress:
Week 1: Audit and Engage
- Check Setup → EAC → Settings to confirm whether you’re on EWS or already on Graph
- Pull the full list of connected users (Setup → Einstein Activity Capture → Connected Accounts)
- Check for Lightning Sync users (Setup → Lightning Sync)
- Open a Microsoft 365 admin ticket for Entra tenant-wide consent
- Audit Activity 360 / Activity Metrics dependencies with sales ops
Week 2: Prep and Test
- Microsoft 365 admin grants Graph API permissions in Entra
- Set up a sandbox EAC migration to test user reconnection flow and automation behavior
- Identify pilot group (10–20 users)
- Draft user communication for reconnection instructions
Week 3: Pilot
- Execute migration for pilot group
- Monitor Sync Health for 5 business days
- Confirm test email logs and calendar events appear correctly in Salesforce
- Confirm no automation regressions on Task/Event/EmailMessage objects
Week 4: Full Rollout
- Migrate remaining users in batches
- Monitor connected accounts daily until all users reconnected
- Update your Salesforce admin runbook to document new Graph API configuration
Full Custom Integration Guide: Microsoft Graph → Salesforce {#full-custom-integration-guide-microsoft-graph—salesforce}
The checklist above covers the standard EAC upgrade path — switching EAC’s protocol from EWS to Graph while keeping EAC as the sync mechanism. For most orgs, that’s the right move before October 1.
But EAC has real architectural limits that the Graph migration doesn’t fix: activity data still lives outside standard Salesforce objects, you still can’t run native reports on email activity, and the 24-month rolling retention window still applies. If you’ve been tolerating those constraints, this is a good time to consider whether to replace EAC entirely with a custom Microsoft Graph integration that writes directly to Salesforce Task and Event objects.
This section is a complete how-to for that path — including architecture choices, Azure setup, Apex code, and a phased rollout plan. It’s more involved than the EAC upgrade (6–8 weeks minimum), but the result is a CRM activity record you actually own.
Why Build Beyond EAC?
EAC has well-documented limitations that the EWS→Graph upgrade doesn’t resolve:
1. Data impermanence and storage limits. EAC activity data has a 24-month rolling retention window. Data older than the retention period is deleted. For compliance-heavy industries (financial services, healthcare, legal), this is a non-starter. EAC storage also counts against org storage limits — a significant cost driver for high-volume email organizations.
2. No native reporting on EAC activities. Because EAC data sits outside standard Salesforce objects, it’s invisible to standard Salesforce Reports & Dashboards. You can’t report on which rep sent how many emails last quarter without Einstein Analytics or a custom workaround.
3. Unreliable sync and matching. EAC’s record-matching algorithm relies on email addresses. When a contact uses multiple addresses or aliases are involved, sync gaps occur silently. There’s no built-in audit trail for failed matches.
4. Limited admin control. Salesforce admins can’t selectively sync emails by subject, domain, attachment type, or custom criteria. It’s on or off, per user. That makes GDPR compliance and legal hold scenarios operationally painful.
5. No Teams, shared mailboxes, or transcripts. The irony: EAC already uses Microsoft Graph under the hood, but it doesn’t expose Graph’s richer capabilities — reading Teams messages, accessing shared mailboxes, querying meeting transcripts, or reading message sensitivity labels. A direct Graph integration unlocks all of it.
What Microsoft Graph API Offers
Microsoft Graph is the unified API layer for Microsoft 365. The relevant capabilities for Salesforce integration:
Core email and calendar:
| Capability | Graph API Endpoint |
|---|---|
| Read user mail | /v1.0/users/{id}/messages |
| Send email on behalf | /v1.0/users/{id}/sendMail |
| Read calendar events | /v1.0/users/{id}/events |
| Create calendar events | POST /v1.0/users/{id}/events |
| List calendar views | /v1.0/users/{id}/calendarView |
Advanced capabilities EAC doesn’t touch:
| Capability | Graph API Endpoint |
|---|---|
| Teams chat messages | /v1.0/users/{id}/chats |
| Meeting transcripts | /v1.0/users/{id}/onlineMeetings/{id}/transcripts |
| Shared mailboxes | /v1.0/users/{sharedMailbox}/messages |
| Message sensitivity labels | message.sensitivity property |
| Mail folders and rules | /v1.0/users/{id}/mailFolders |
| Attachments with metadata | /v1.0/users/{id}/messages/{id}/attachments |
Key technical advantages:
- Webhooks (change notifications): Graph supports push-based subscriptions instead of polling — real-time activity capture without lag
- Batch requests: Graph’s
$batchendpoint allows bundling up to 20 API calls, critical for high-volume orgs - Delta queries:
/messages/deltareturns only changed items since last sync — dramatically more efficient than full syncs - Full permissions granularity: Application permissions (
Mail.Read,Calendars.ReadWrite) vs. delegated — you control the consent surface
Architecture: What You’re Building
A direct Microsoft Graph → Salesforce integration replaces EAC with a custom pipeline:
Microsoft 365 (Graph API)
↓ [Webhook change notifications or scheduled delta queries]
Integration Middleware
↓ [Transform + Match + Deduplicate]
Salesforce (Task/Event objects or Custom Objects)
Option A: Salesforce Flow + External Service Use a Salesforce External Service registration with an OpenAPI spec pointing to a middleware endpoint. Flows call the external service to pull or push Graph data. Low-code but limited throughput.
Option B: Apex + Named Credentials Build Apex callouts using a Salesforce Named Credential configured with OAuth 2.0 JWT Bearer flow for Microsoft 365. Handles auth automatically, works with Apex Scheduled Jobs and triggers. Best for mid-complexity orgs.
Option C: Dedicated Integration Platform (MuleSoft, Boomi, Azure Logic Apps) Best for enterprise scale. The integration platform handles Graph webhooks, transformation logic, deduplication, and writes to Salesforce via REST/Bulk API. Recommended for orgs with >500 sales reps or >1M activities/month.
Option D: Azure Functions + Salesforce REST API Serverless middleware using Azure Functions as the webhook consumer. Lightweight, cost-effective, and keeps the integration within the Microsoft ecosystem. Good for Microsoft-heavy orgs.
Phase 1: Pre-Migration Audit (Week 1–2)
1.1 EAC Usage Assessment
Salesforce Admin → Setup → Einstein Activity Capture → Settings
- Note: How many users are connected?
- Note: What's the current retention period?
- Export user list and their sync configuration
1.2 Data Inventory
- Query the Activity Timeline for each major record type (Accounts, Opportunities, Contacts)
- Identify the volume of EAC-sourced activities
- Flag any compliance-sensitive email threads
1.3 Microsoft 365 Tenant Assessment
- Confirm Microsoft 365 license tier (Graph availability requires Business or Enterprise)
- Identify the Azure AD app registration process owner
- Audit current Outlook shared mailboxes used by the sales team
Phase 2: Azure AD App Registration (Week 2)
2.1 Create App Registration
- Go to Azure Portal → Azure Active Directory → App registrations → New registration
- Set name:
Salesforce Activity Sync - Supported account types: Single tenant (your organization only)
- Redirect URI: Leave blank for daemon/service app
2.2 Configure API Permissions
Add the following Application permissions (not delegated):
Microsoft Graph:
- Mail.Read (read all user mailboxes)
- Calendars.Read (read all user calendars)
- Calendars.ReadWrite (if syncing back to Outlook)
- User.Read.All (resolve user identities)
Warning: Application permissions grant access to ALL users’ mailboxes in your tenant. This requires Global Admin consent — not just Exchange Admin. Document this access model in your data privacy impact assessment before requesting consent. In regulated industries, this often requires a security review before the ticket even gets opened.
2.3 Create Client Secret or Certificate
- Certificates > Upload a certificate (preferred for production)
- Or: Client secrets > New client secret (simpler, rotate every 12 months)
- Save the secret/certificate — it won’t be shown again
2.4 Record Tenant and App Details
Tenant ID: [Your Azure AD Tenant ID]
Client ID: [Application (client) ID]
Client Secret: [Value from step 2.3]
Phase 3: Salesforce Named Credential Setup (Week 2–3)
3.1 Create Auth Provider
Setup → Auth. Providers → New
Type: OpenID Connect
Name: Microsoft Graph Auth
Consumer Key: [Client ID from Azure]
Consumer Secret: [Client Secret]
Authorize Endpoint: https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize
Token Endpoint: https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token
Default Scopes: https://graph.microsoft.com/.default
3.2 Create Named Credential
Setup → Named Credentials → New
Label: Microsoft Graph API
Name: Microsoft_Graph_API
URL: https://graph.microsoft.com
Identity Type: Named Principal
Authentication Protocol: OAuth 2.0
Auth. Provider: [Auth Provider from 3.1]
Phase 4: Core Apex Integration Layer (Week 3–5)
4.1 Email Fetch Service
public class GraphEmailService {
private static final String GRAPH_ENDPOINT = 'callout:Microsoft_Graph_API';
/**
* Fetch emails for a user since a given datetime, using delta queries for efficiency.
*/
public static List<GraphEmail> fetchUserEmailsDelta(String userId, String deltaLink) {
String endpoint = String.isNotBlank(deltaLink)
? deltaLink
: GRAPH_ENDPOINT + '/v1.0/users/' + userId
+ '/messages/delta?$select=id,subject,from,toRecipients,ccRecipients,sentDateTime,'
+ 'receivedDateTime,bodyPreview,conversationId&$top=100';
Http http = new Http();
HttpRequest req = new HttpRequest();
req.setEndpoint(endpoint);
req.setMethod('GET');
req.setHeader('Content-Type', 'application/json');
HttpResponse res = http.send(req);
if (res.getStatusCode() != 200) {
throw new GraphIntegrationException('Graph API error: ' + res.getStatus()
+ ' — ' + res.getBody());
}
GraphEmailResponse parsed = (GraphEmailResponse) JSON.deserialize(
res.getBody(), GraphEmailResponse.class
);
// Store delta link for next run
if (String.isNotBlank(parsed.odataDeltaLink)) {
storeDeltaLink(userId, parsed.odataDeltaLink);
}
return parsed.value;
}
/**
* Match email to Salesforce records by sender/recipient email address.
*/
public static Id matchEmailToRecord(String emailAddress) {
// Try Contact first
List<Contact> contacts = [
SELECT Id FROM Contact
WHERE Email = :emailAddress
LIMIT 1
];
if (!contacts.isEmpty()) return contacts[0].Id;
// Try Lead
List<Lead> leads = [
SELECT Id FROM Lead
WHERE Email = :emailAddress
AND IsConverted = false
LIMIT 1
];
if (!leads.isEmpty()) return leads[0].Id;
return null;
}
private static void storeDeltaLink(String userId, String deltaLink) {
// Upsert to a custom metadata or custom setting record
Graph_Sync_State__c state = new Graph_Sync_State__c(
User_Id__c = userId,
Delta_Link__c = deltaLink,
Last_Sync__c = DateTime.now()
);
upsert state User_Id__c;
}
public class GraphEmailResponse {
public List<GraphEmail> value;
@AuraEnabled public String odataDeltaLink;
@AuraEnabled public String odataNextLink;
}
public class GraphEmail {
public String id;
public String subject;
public GraphEmailAddress from;
public List<GraphEmailAddress> toRecipients;
public DateTime sentDateTime;
public DateTime receivedDateTime;
public String bodyPreview;
public String conversationId;
}
public class GraphEmailAddress {
public GraphAddress emailAddress;
}
public class GraphAddress {
public String name;
public String address;
}
public class GraphIntegrationException extends Exception {}
}
4.2 Activity Writer Service
public class SalesforceActivityWriter {
/**
* Convert a Graph email to a Salesforce Task on the matched record.
*/
public static Task createEmailTask(GraphEmailService.GraphEmail email, Id whoId) {
Task t = new Task();
t.Subject = 'Email: ' + (email.subject ?? '(No Subject)');
t.Description = email.bodyPreview;
t.ActivityDate = email.sentDateTime?.date() ?? Date.today();
t.Status = 'Completed';
t.Priority = 'Normal';
t.Type = 'Email';
t.WhoId = whoId;
t.CallType = null;
// Store Graph message ID to prevent duplicates
t.Graph_Message_Id__c = email.id; // Custom field: Text(255), ExternalId=true
t.Conversation_Id__c = email.conversationId;
return t;
}
/**
* Bulk upsert tasks, using Graph Message ID as external ID to prevent duplicates.
*/
public static void upsertEmailTasks(List<Task> tasks) {
if (tasks.isEmpty()) return;
Schema.SObjectField extIdField = Task.Graph_Message_Id__c;
List<Database.UpsertResult> results = Database.upsert(tasks, extIdField, false);
for (Integer i = 0; i < results.size(); i++) {
if (!results[i].isSuccess()) {
System.debug(LoggingLevel.WARN, 'Failed to upsert task: '
+ results[i].getErrors()[0].getMessage());
}
}
}
}
Phase 5: Sync Scheduler and Monitoring (Week 5–6)
5.1 Schedulable Apex Job
global class GraphSyncScheduler implements Schedulable {
global void execute(SchedulableContext sc) {
// Get list of active sync users
List<Graph_Sync_User__c> users = [
SELECT User_Id__c, Salesforce_User_Id__c, Delta_Link__c
FROM Graph_Sync_User__c
WHERE Is_Active__c = true
];
for (Graph_Sync_User__c u : users) {
// Enqueue each user's sync as an async job
System.enqueueJob(new GraphUserSyncJob(u.User_Id__c, u.Salesforce_User_Id__c));
}
}
}
5.2 Schedule the Job
// Run every 15 minutes
String cronExpr = '0 0/15 * * * ?';
System.schedule('Graph Email Sync', cronExpr, new GraphSyncScheduler());
5.3 Monitoring Custom Object
Create Graph_Sync_Log__c with fields:
Run_Timestamp__c(DateTime)User_Count__c(Number)Emails_Synced__c(Number)Errors__c(Long Text)Status__c(Picklist: Success, Partial, Failed)
Phase 6: Cutover and EAC Decommission (Week 7–8)
6.1 Run in Parallel
For 2 weeks, run both EAC and the Graph integration simultaneously. Compare activity counts per user per day.
6.2 Validation Checklist
- Email tasks appearing in Activity Timeline ✓
- Tasks correctly linked to Contact/Lead (WhoId) ✓
- No duplicate tasks for same email ✓
- Calendar events syncing (if in scope) ✓
- Delta sync working (not full sync on every run) ✓
- Monitoring dashboard shows 0 errors over 48h ✓
6.3 Disable EAC
Setup → Einstein Activity Capture → Settings → Deactivate
Disconnect all user accounts. Confirm EAC sync stops appearing in timeline.
6.4 Data Migration (Optional)
EAC historical activities (Task/Event objects created by EAC) remain. Activities in the Activity Data Lake cannot be migrated to standard objects without a custom export — treat this as a data lineage decision with your compliance team, not an afterthought.
Advanced Patterns
Webhook-based real-time sync
Instead of polling every 15 minutes, subscribe to Graph change notifications:
POST https://graph.microsoft.com/v1.0/subscriptions
{
"changeType": "created,updated",
"notificationUrl": "https://your-middleware.example.com/graph-webhook",
"resource": "users/{userId}/messages",
"expirationDateTime": "2026-09-01T00:00:00Z",
"clientState": "your-secret-token"
}
Your middleware receives POST notifications and pushes to Salesforce via REST API. This gets email-to-Salesforce latency below 30 seconds.
Teams message capture
With the Graph integration in place, extending to Teams is straightforward:
GET /v1.0/users/{userId}/chats
GET /v1.0/chats/{chatId}/messages
Map Teams messages to Salesforce Tasks with Type = 'Chat'. This creates a complete omnichannel activity record — something EAC never offered.
Common Pitfalls
| Pitfall | Impact | Solution |
|---|---|---|
| Using delegated permissions instead of application permissions | Sync breaks when user sessions expire | Always use Application permissions with admin consent |
| Not implementing delta queries | API throttling, slow sync, high token cost | Always use /delta endpoints, store delta links |
| Missing external ID on Task | Duplicate activities created on every run | Add Graph_Message_Id__c as external ID field |
| Syncing all emails including internal | Noise in activity timeline, storage bloat | Filter by domain: exclude @yourcompany.com senders |
| No error monitoring | Silent failures go undetected for days | Build Graph_Sync_Log__c from day one |
| Ignoring rate limits | 429 errors causing dropped activities | Implement exponential backoff; respect Retry-After header |
Cost and Licensing
Microsoft Graph API: Included with Microsoft 365 Business Premium, E3, and E5. No additional per-call cost within fair use limits. High-volume orgs (>10,000 messages/user/day) should review Microsoft’s throttling documentation.
Salesforce licensing: Custom objects and Apex usage are included with Enterprise and above. Verify API call limits if using REST API heavily — consider Salesforce Bulk API 2.0 for high-volume writes.
Integration platform costs (if Option C):
| Platform | Pricing Model |
|---|---|
| MuleSoft | Licensing separate from Salesforce; contact Salesforce for bundle pricing |
| Azure Logic Apps | ~$0.000025 per action execution; typical org costs $5–50/month |
| Boomi | Subscription-based; contact Dell Technologies |
Bottom Line
You missed the August 3 soft deadline. You still have until October 1 before anything actually breaks. That’s enough time — but only if you start the Microsoft 365 admin engagement this week.
The Entra consent step is the longest lead-time item in this migration; everything else is a few hours of Salesforce Setup work. Don’t assume “nothing’s broken yet” means EWS will keep working. Microsoft’s EWS retirement is not Salesforce’s idea of a deadline — it’s a hard infrastructure change from Microsoft, and Salesforce can’t extend it.
If you’re just running the EAC upgrade: get the Microsoft 365 admin ticket opened today, run your audit this week, and complete the pilot rollout before September 15 so you have clean sync heading into Q4. That’s the 4-week window you have left.
If you’re evaluating a full custom Graph integration: the 6–8 week build timeline means you’ll need to complete the EAC EWS→Graph upgrade as an interim measure anyway, then begin the custom integration work in parallel. The two paths aren’t mutually exclusive — you can upgrade EAC now and replace it in Q1 2027 with a permanent solution.
Either way, open the ticket today.
Discussion