Business Problem
The CRM's email integration polled every connected Microsoft 365 mailbox on a 15-minute schedule via IMAP. Sales reps would call a lead, and the email thread context in the CRM was up to 15 minutes stale, which produced embarrassing double-contacts and made the timeline feature untrustworthy. Polling 3,000 mailboxes also burned worker capacity: over 280k IMAP sessions a day, most returning nothing new.
Microsoft was simultaneously deprecating basic authentication for IMAP, putting a hard deadline on the whole approach. The business needed near-real-time sync, modern OAuth2 authentication, and two-way capability so emails sent from the CRM appeared correctly in the user's Sent Items. Reliability mattered more than raw speed: losing a customer email silently was the one unforgivable failure.
Architecture
I rebuilt the integration on Microsoft Graph change notifications. Each connected mailbox got a Graph subscription; when mail arrived, Graph pushed a webhook within seconds, and a lightweight receiver validated it and enqueued a fetch job. Workers pulled full message content via Graph delta queries, matched messages to CRM contacts and deals, and wrote them to the timeline. Median arrival-to-timeline latency landed under 5 seconds.
Because webhooks are inherently lossy, delivery is at-most-once from your perspective if anything hiccups, I paired them with a delta-query reconciliation sweep. Every mailbox ran a delta sync every 30 minutes using stored delta tokens, catching anything a missed webhook dropped. Webhooks provided speed; delta queries provided the correctness guarantee.
OAuth2 token management was centralized in a token service handling refresh, encryption at rest, and admin-consent flows for organization-wide connections. Subscription lifecycle management ran as a scheduled process, since Graph subscriptions expire every ~3 days and renewal failures were the top cause of silent sync death in early testing.
System Design
Webhooks for latency, delta-query reconciliation for completeness, a deliberate two-layer design where neither mechanism needs to be perfect.
Idempotent message ingestion keyed on Graph immutable IDs, so webhook/delta overlap never produced duplicate timeline entries.
Subscription lifecycle manager with renewal at 70% of TTL, health scoring per mailbox, and automatic resubscribe-plus-backfill on failure.
Per-tenant Graph throttling budget with adaptive backoff honoring Retry-After headers, keeping the app out of Microsoft's penalty box during bulk onboarding.
Encrypted token vault with automatic refresh and proactive expiry alerts routed to customer success before users noticed anything.
Contact-matching pipeline with confidence tiers: exact address match auto-linked, fuzzy matches queued for one-click user confirmation.
My Responsibilities
Owned the integration end to end: architecture, Graph API design decisions, implementation, and production operation.
Built the webhook receiver, delta reconciliation engine, and subscription lifecycle manager.
Implemented OAuth2 authorization code and admin-consent flows, including the token service and encryption model.
Designed the migration that moved 3,000+ mailboxes from IMAP polling to Graph with zero user re-authentication where tenant admins granted consent.
Wrote the throttling strategy after profiling Graph's per-app and per-mailbox limits against our real traffic shape.
Built operational dashboards for per-mailbox sync health that customer success used directly, cutting escalations to engineering by more than half.
Implementation
I started with a two-week spike against a test tenant purely to learn Graph's failure modes: subscription expiry behavior, throttling response shapes, delta token invalidation cases. That spike produced a failure-mode catalog that drove the design more than the happy-path docs did.
The rollout ran in three rings: internal mailboxes, 50 volunteer customers, then general migration in tenant-sized batches. Each ring ran the new pipeline in parallel with legacy polling, with a comparator flagging any message one system saw and the other missed. Ring two caught the delta-token invalidation case that occurs when a mailbox moves between Exchange servers, which we then handled with automatic full resync.
The final migration was mostly a consent problem, not a code problem. I built an admin-consent flow so a tenant administrator could authorize all their organization's mailboxes at once, which converted what could have been 3,000 individual user re-auth requests into about 90 admin approvals.
Results
Email sync latency dropped from up to 15 minutes to under 5 seconds median, under 12 seconds p99.
Outbound Graph/IMAP call volume fell roughly 70% while message throughput grew to 250k messages per day.
Zero confirmed lost messages after full rollout, verified by the reconciliation layer across 18 months of operation.
Silent sync failures went from the top support category for the email feature to zero recorded incidents.
Completed migration off basic-auth IMAP five weeks ahead of Microsoft's deprecation deadline.
Timeline feature engagement rose 45% as reps started trusting its freshness.
Lessons Learned
Webhooks are a latency optimization, never a source of truth; always pair them with a pull-based reconciliation loop.
Spike on failure modes before designing, third-party API docs describe the happy path, production teaches you everything else.
Subscription and token lifecycle deserve the same monitoring rigor as request paths; most integration outages are lifecycle bugs.
Admin-consent flows are a massive UX and migration lever for B2B integrations; invest in them early.
Future Improvements
Extend the same architecture to calendar and contact sync, reusing the subscription manager and reconciliation engine.
Add Google Workspace support behind the same internal sync interface to make provider choice invisible to product code.
Move webhook receivers to Azure Functions for scale-to-zero economics on the spiky notification traffic.
Use message classification to auto-file support-related emails to the ticketing integration.
Challenges
Silent subscription death
Graph subscriptions expire roughly every three days, and a failed renewal means a mailbox silently stops syncing. I treated subscription health as a first-class monitored resource: renewals at 70% TTL, a watchdog comparing expected-vs-received webhook volume per mailbox, and automatic resubscribe with delta backfill. Silent-death incidents went from weekly to zero.
Graph throttling during bulk onboarding
Onboarding a 400-mailbox tenant triggered 429 storms that degraded sync for existing customers. I introduced per-tenant concurrency budgets, honored Retry-After strictly, and spread initial historical backfills over off-peak hours. Bulk onboarding stopped affecting steady-state customers entirely.
Duplicate and self-referencing messages
Emails sent from the CRM via Graph would echo back through the webhook as new inbound mail. Tagging outbound messages with an internet-message header and matching on immutable IDs at ingestion made the pipeline fully idempotent, eliminating duplicate timeline entries that had plagued the beta.