Email Sync API: How Email Synchronization Works for SaaS Products
Build SaaS features that sync user inboxes in real time. Connect Gmail, Outlook, and IMAP through a single email sync API, with webhooks, OAuth flows, and full folder access included.
const UnipileClient = require('@unipile/node-sdk');
const client = new UnipileClient({
dsn: process.env.UNIPILE_DSN,
token: process.env.UNIPILE_TOKEN
});
// Fetch synced emails from linked account
const emails = await client.email.listEmails({
account_id: 'acc_gmail_xyz',
folder: 'INBOX',
limit: 50
});
// Real-time: register webhook
await client.webhook.create({
url: 'https://yourapp.com/webhooks/email',
events: ['email.new', 'email.read']
});What is an Email Sync API?
An email sync API is a programmatic interface that lets your application continuously mirror a user's mailbox - reading new messages, tracking status changes (read/unread, moved, deleted), and reflecting folder structure - without the user manually exporting data. Unlike a send-only API, an email synchronization API maintains a live, two-way replica of the inbox inside your product.
At the protocol level, each provider exposes its own synchronization primitive: Gmail uses history.list with a historyId cursor, Microsoft Graph uses delta queries on the /messages/delta endpoint, and standard IMAP servers support the IDLE command for push-style notifications. A unified email sync API like Unipile abstracts these three protocols behind one endpoint, so your team writes the sync logic once and ships to all providers.
If you are building a CRM, a sales engagement tool, an AI email assistant, or any SaaS that needs live inbox data, an email sync API is the foundation. For sending transactional emails (password resets, receipts), that is a different category - see our full email API guide or our dedicated sync vs transactional comparison.
Email Sync vs Email Send: Why the Distinction Matters
Developers often conflate email sync APIs with transactional email APIs. They serve opposite purposes. Choosing the wrong category sets your architecture back by weeks.
Reads and mirrors a user's existing mailbox into your application. Requires the user to authorize access to their account (OAuth or IMAP credentials). Your app becomes a secondary reader of their inbox.
Used by: CRMs, sales engagement tools, AI email assistants, helpdesks, email archiving, inbox analytics.
Sends system-generated emails from your own domain. No user authorization needed. You authenticate as the sender (API key), not as the user. Examples: SendGrid, Mailgun, Resend, Amazon SES.
Unipile is NOT in this category. Unipile is the sync side - reading and mirroring user inboxes via OAuth.
Who Needs an Email Sync API?
Any SaaS product that needs to show, analyze, or act on a user's incoming email relies on an email sync API. These are the five most common use cases we see in production.
Automatically log every inbound and outbound email against the correct contact record. Sales reps stop copy-pasting emails; the CRM becomes the system of record in real time. No manual BCC forwarding required.
Email API guideTrack reply rates, detect out-of-office responses, and trigger follow-up sequences based on inbox events. An email sync API gives your sequences real-time awareness of what's happening in the prospect's inbox.
Send email programmaticallyFeed a live email stream to an LLM agent that drafts replies, categorizes incoming messages, extracts action items, or routes tickets. The agent needs a continuous sync feed, not a one-time export. A real-time email sync API is mandatory.
Read emails programmaticallyConvert customer emails into support tickets automatically, including all thread context and attachments. Sync the ticket status back when the agent replies, so the customer's inbox reflects the resolution thread.
Compare email API providersCapture every inbound and outbound email for compliance, legal discovery, or analytics. An email synchronization API lets you build a queryable archive of all corporate communications without touching the mail server directly.
Free email API tierAnalyze email volume, response times, conversation patterns, and sentiment across a team's linked accounts. Marketing, operations, and finance teams use inbox analytics to measure communication quality and identify bottlenecks.
Email API full guideHow Email Synchronization Works: OAuth, Delta Sync, and Webhooks
An email sync API is more than a read endpoint. It is a stateful pipeline that authenticates users, maintains token freshness, tracks mailbox state, and delivers changes in near real time. Here is what happens at each layer.
The user clicks "Connect your inbox" in your app. They are redirected to Google or Microsoft's consent screen, where they approve the scopes your application requests. For Gmail that means gmail.readonly or gmail.modify; for Outlook it means Mail.Read or Mail.ReadWrite. After consent, your app receives an access token (valid 1 hour for Google, 1 hour for Microsoft) and a refresh token (long-lived). IMAP accounts use username + password or OAuth depending on the provider's configuration.
On first sync, the email sync API performs a full backfill: it fetches the folder list (INBOX, Sent, Drafts, custom labels) and pulls recent messages up to a configurable history depth. For Gmail, this uses users.messages.list with pagination. For Microsoft Graph, it uses GET /me/messages. For IMAP, it issues a SELECT INBOX followed by a FETCH range. The snapshot gives your database its baseline state, including message IDs and thread groupings.
After the initial snapshot, polling the full mailbox repeatedly would waste quota and slow your app. Delta sync is the solution. Gmail provides a historyId cursor: you call users.history.list with the last known historyId and receive only the changes (new messages, label changes, deletions) since that point. Microsoft Graph uses GET /me/messages/delta with a $deltaToken. IMAP uses UID FETCH with a CHANGEDSINCE modifier (CONDSTORE extension). This delta sync pattern keeps API calls minimal even for high-volume mailboxes.
Access tokens expire. Your email sync infrastructure must detect 401 Unauthorized responses, use the refresh token to obtain a new access token from Google or Microsoft, and retry the failed request. This must happen transparently, without interrupting the user's session. Refresh tokens themselves can be revoked - by the user, by an admin policy, or after 6 months of inactivity (Google) - so your system needs to detect revocation and prompt the user to re-authorize.
Polling on a schedule introduces latency - a 30-second poll means emails can be up to 30 seconds stale. For real-time inbox features, the email sync API must support push notifications. Gmail uses Google Cloud Pub/Sub: you register a topic and Gmail publishes a notification whenever the historyId advances. Microsoft Graph uses change notifications on the /me/mailFolders/inbox/messages resource. A unified email sync API (like Unipile) normalizes these into a single webhook event - email.new - delivered to your endpoint regardless of provider.
Native Email Sync APIs: Gmail, Microsoft Graph, and IMAP
Each of the three email providers exposes a different sync primitive. Understanding the differences tells you why building a multi-provider email sync API from scratch takes months, not days.
Gmail's sync model is built around the historyId cursor. After your initial sync, every subsequent call to users.history.list returns only changes since your last known historyId - new messages, label additions, label removals, and deletions.
For real-time push, Gmail requires you to set up a Google Cloud Pub/Sub topic and call users.watch to register it. Gmail then publishes a notification (containing a new historyId) to your topic whenever the mailbox changes. Your worker subscribes to the topic and calls history.list to fetch the actual changes.
Rate limits: 1 billion quota units per day per project, with per-user limits. users.messages.get costs 5 units; users.history.list costs 2 units. For a multi-tenant app, quota management becomes a full-time concern. See the email API guide for more.
from googleapiclient.discovery import build
# Delta sync using historyId cursor
def fetch_changes(service, history_id):
result = service.users().history().list(
userId='me',
startHistoryId=history_id,
historyTypes=[
'messageAdded',
'messageDeleted',
'labelAdded'
]
).execute()
return result.get('history', [])Microsoft Graph uses delta queries on the /me/messages/delta endpoint. The first call returns a full page of messages plus a @odata.deltaLink. Subsequent calls to that delta link return only changed messages since the previous call - new, modified, and deleted items.
For real-time push, Microsoft Graph supports change notifications via webhooks. You register a subscription on /me/mailFolders/inbox/messages with a notificationUrl. Microsoft sends a POST to your URL when messages change. Subscriptions must be renewed every 4230 minutes (about 3 days) or they expire.
Note: This covers both personal Outlook accounts and Microsoft 365 / Exchange Online - they use the same Graph API. See the Microsoft Graph email integration guide for details on app registration and admin consent.
// Microsoft Graph delta sync
async function fetchDeltaMessages(client, deltaLink) {
const url = deltaLink
|| '/me/messages/delta';
const res = await client
.api(url)
.select('id,subject,from,receivedDateTime')
.get();
return {
messages: res.value,
nextDelta: res['@odata.deltaLink']
};
}IMAP (RFC 3501) predates modern sync APIs by decades. It exposes per-folder sequence numbers and UIDs. For delta sync, the CONDSTORE extension (RFC 7162) adds a MODSEQ value to each message, letting you fetch only messages with a MODSEQ higher than your last known value via UID FETCH * (FLAGS) (CHANGEDSINCE modseq).
For real-time push, IMAP supports the IDLE command (RFC 2177). Your client sends IDLE and the server pushes EXISTS or EXPUNGE responses when the folder changes - no polling needed. Most IMAP servers support IDLE; connections must be refreshed every 29 minutes to prevent timeouts.
IMAP is critical because it covers any mail server not managed by Google or Microsoft: corporate Exchange (on-premise), ProtonMail, Zoho, Fastmail, and custom domains. See the IMAP integration guide for a full implementation walkthrough.
const Imap = require('imap');
const imap = new Imap({ host, port: 993, tls: true });
imap.once('ready', () => {
imap.openBox('INBOX', true, () => {
imap.on('mail', (numNew) => {
// IDLE push: new mail arrived
fetchNewMessages(numNew);
});
});
});Email API Feature Coverage by Provider
A single Unipile integration gives you access to all email operations across Gmail, Outlook, and IMAP providers. Click any provider heading to read the full integration guide.
| Feature | Gmail | Outlook / M365 | IMAP / SMTP |
|---|---|---|---|
| Authentication | |||
| OAuth2 (no password storage) | ✓ | ✓ | App password |
| Hosted auth / consent flow | ✓ | ✓ | ✓ |
| Automatic token refresh | ✓ | ✓ | ✓ |
| Email Operations | |||
| Send email from user account | ✓ | ✓ | ✓ |
| Read & list emails | ✓ | ✓ | ✓ |
| Send with attachments | ✓ | ✓ | ✓ |
| Reply in existing thread | ✓ | ✓ | ✓ |
| Draft management | ✓ | ✓ | ✓ |
| Labels / Folders | ✓Labels | ✓Folders | ✓Folders |
| Daily send limit (approx.) | ~500 / day | ~10,000 / day | Server-dependent |
| Sync & Events | |||
| Real-time webhooks | ✓ | ✓ | ✓ |
| Incremental delta sync | ✓ | ✓ | ✓ |
| Thread grouping | ✓ | ✓ | ✓ |
| SOC 2 Type II / CASA Tier 2 | ✓ | ✓ | ✓ |
Skip the Gmail + Graph + IMAP boilerplate. One email sync API covers all three.
Unipile's unified email sync API connects to Gmail, Outlook, and IMAP through a single endpoint. OAuth flows, token refresh, delta sync, and webhooks - all handled for you. Starts free, no credit card required.
The Hidden Complexity of Email Sync at Scale
Building a proof-of-concept email sync API takes a weekend. Building one that is reliable in production with 1,000 linked accounts takes months. Here is what nobody tells you upfront.
Gmail enforces per-user quotas (250 quota units/second) and per-project daily limits. Microsoft Graph throttles at 10,000 requests/10 minutes per app. With 500 linked accounts all syncing on schedule, you need a distributed rate-limiter with exponential backoff, jitter, and per-account queue isolation. A single burst from one account can exhaust quota for all others.
Every linked account has an access token that expires. At 1,000 accounts, you can expect dozens of concurrent token refreshes during peak sync windows. A single failed refresh cascades into missed sync cycles. You need a dedicated token lifecycle service with retry logic, revocation detection, and an alerting pipeline to prompt users to re-authorize when refresh tokens expire.
Gmail uses labels (a message can have multiple labels simultaneously). Outlook uses folders (hierarchical, with move operations). IMAP uses folders too but with namespaces that vary by server vendor. Normalizing these into a consistent folder model for your app requires provider-specific mapping logic. Edge cases include shared mailboxes, delegated access, and Gmail's "All Mail" vs "Inbox" distinction.
Email attachments can be large. Fetching and storing attachments for every synced email across thousands of accounts adds up quickly in both bandwidth and storage costs. You need a streaming pipeline that only downloads attachments on demand, deduplicated storage, and a CDN layer for serving them from your product. MIME parsing itself introduces bugs - multi-part emails, quoted-printable encoding, and inline attachments each need specific handling.
Gmail threads by threadId - a server-side concept. IMAP has no native threading; you reconstruct threads using the References and In-Reply-To headers (RFC 5322). Outlook has conversationId. Normalizing threads across providers - especially for cross-provider replies - requires fallback heuristics based on subject normalization and message-ID chains.
Gmail Pub/Sub notifications are not guaranteed - missed messages during downtime are not re-sent. Microsoft Graph webhook subscriptions expire and must be renewed. If your webhook receiver is down during a push notification, you miss the event and fall back to polling. A production email sync API needs a reconciliation loop that periodically catches up missed events using the delta sync cursor, independent of webhook availability.
3 Email Sync Architectures Compared
Teams building email sync features typically evaluate three implementation patterns. Here is an honest comparison of each, from build effort to ongoing maintenance cost.
Sync Emails with Unipile's Unified Email Sync API
Unipile's email sync API covers Gmail, Outlook, and IMAP through one unified interface. OAuth flows, token refresh, delta sync, and webhook delivery are all managed for you - your team ships the feature, not the infrastructure.
Sign up at dashboard.unipile.com. The free email API tier gives you access to all providers with no credit card required. You get your DSN (Data Source Name) and API token immediately.
Use Unipile's hosted OAuth flow to let your user authorize access to their Gmail or Outlook account. For IMAP, collect their credentials and pass them to POST /accounts. Unipile handles the OAuth redirect, token exchange, and stores the refresh token securely.
Call GET /emails with the account_id of the linked account. Unipile runs the delta sync against Gmail's history.list, Microsoft Graph's delta endpoint, or IMAP's CONDSTORE - you always get the same normalized JSON response regardless of provider.
POST your endpoint URL to Unipile's webhook API. When a new email arrives in any linked account - whether Gmail, Outlook, or IMAP - Unipile delivers a normalized email.new event to your URL. No Pub/Sub setup, no Graph subscription renewal, no IDLE connection management. See the email API guide for full event reference.
Call GET /emails/{id} to retrieve the full message body (HTML and plain text), headers, MIME parts, and attachment references. Attachments are served via signed URLs - you never have to store raw MIME data yourself. See read emails programmatically for examples.
const axios = require('axios');
const DSN = process.env.UNIPILE_DSN;
const TOKEN = process.env.UNIPILE_TOKEN;
const api = axios.create({
baseURL: `https://${DSN}/api/v1`,
headers: { 'X-API-KEY': TOKEN }
});
// Step 3 - List synced emails
async function listEmails(accountId) {
const { data } = await api.get('/emails', {
params: {
account_id: accountId,
folder: 'INBOX',
limit: 20
}
});
return data.items;
}
// Step 4 - Register webhook
async function registerWebhook() {
await api.post('/webhooks', {
url: 'https://yourapp.com/api/email-events',
events: ['email.new', 'email.updated']
});
}
// Step 5 - Get full email content
async function getEmail(emailId) {
const { data } = await api.get(`/emails/${emailId}`);
return data;
}Stop rebuilding the same email sync pipeline for every provider.
Connect Gmail, Outlook, and IMAP through one email synchronization API. Real-time webhooks, delta sync, and OAuth token management - all handled. Your team focuses on the product, not the plumbing.
Real-Time Email Sync: Webhooks vs Polling vs IMAP IDLE
Choosing the wrong real-time mechanism for your email synchronization API adds latency, burns quota, or leaves your app blind during outages. Here is a direct comparison of the three approaches.
| Approach | How it works | Latency | Best for | Complexity |
|---|---|---|---|---|
| Webhooks (push) | Provider sends HTTP POST to your endpoint when a mailbox changes. Gmail uses Pub/Sub; Microsoft Graph uses change notifications. | Under 5s | Gmail, Outlook, unified APIs like Unipile | Medium - subscription management required |
| Polling (scheduled) | Your worker calls the provider API on a schedule (every 30s, 1min, 5min) and fetches changes using a delta cursor. | 30s-5min | All providers, simple setups | Low - but quota-intensive at scale |
| IMAP IDLE (long-poll) | Your client sends IDLE to the server; the server pushes EXISTS notifications when new mail arrives. Connection held open up to 29 min. | Under 1s | IMAP servers only | High - one TCP connection per mailbox |
Production recommendation: Use webhooks as your primary real-time mechanism and run a delta-sync polling fallback (every 5 minutes) to catch missed events during downtime. With Unipile's email sync API, both are configured once - the unified email.new webhook fires regardless of whether the account is Gmail, Outlook, or IMAP, and a background reconciliation loop handles missed events automatically.
Security and Compliance for Email Sync APIs
Accessing user inboxes creates significant security and regulatory obligations. Here is what your email synchronization API implementation must address before going to production.
Request only the scopes your application needs. For read-only email sync, request gmail.readonly instead of gmail.modify. For Microsoft Graph, request Mail.Read instead of Mail.ReadWrite. Google's CASA verification (required for apps with 100+ users) reviews your requested scopes closely - over-scoping delays approval.
OAuth refresh tokens are long-lived credentials that grant full mailbox access. They must be stored encrypted at rest (AES-256 minimum) and never logged. Rotate your token encryption keys periodically. A refresh token compromise is equivalent to a password compromise for the connected email account.
Email content synced from EU users is personal data under GDPR. You need a legal basis for processing (typically the user's explicit consent via OAuth), a data processing agreement with your email sync API provider, and a clear data retention policy. If your infrastructure is US-based, ensure you have SCCs or an equivalent transfer mechanism for EU data.
Any application using Gmail OAuth scopes with more than 100 users must complete Google's CASA (Cloud Application Security Assessment). This is a security review of your application, including code, infrastructure, and OAuth scope justification. The process takes 4-8 weeks. Start early - failing CASA means losing Gmail access for all users until you pass.
Always verify the signature on incoming webhook payloads from your email sync API. An unsigned or improperly verified webhook can be spoofed to inject fake email events into your application. Unipile signs all webhook deliveries with HMAC-SHA256. Verify the X-Unipile-Signature header before processing any event.
Log every action your application performs on synced email data: who accessed which messages, when, and what was done with the data. Audit logs are required for SOC 2 Type II compliance and are often the first thing enterprise customers ask for during security reviews. Retain logs for at minimum 90 days, ideally 1 year.
Common Pitfalls When Building an Email Sync API
These are the bugs and architectural mistakes we see most often in email synchronization API implementations. All of them are preventable with the right design choices up front.
When a refresh token expires or is revoked, a naive implementation throws an error and stops syncing - silently. The user doesn't know their inbox stopped syncing until they notice stale data. Fix: implement a revocation detection layer that catches invalid_grant errors, marks the linked account as requiring re-authorization, and notifies the user via your product's notification system.
Polling every 10 seconds across 200 linked accounts burns through Gmail's per-project quota within hours. Microsoft Graph starts returning 429 Too Many Requests. The result is silent sync failures for all accounts - not just the ones that triggered the throttle. Fix: use webhooks as your primary mechanism with a 5-minute polling fallback, and implement per-account rate limiting with exponential backoff on all 429 responses.
Raw MIME is large, hard to query, and expensive to parse on read. A single email with attachments can be hundreds of kilobytes. Fix: parse the MIME on ingest: extract HTML body, plain text fallback, headers, and attachment metadata separately. Store attachment binaries in object storage (S3 or equivalent), not in your primary database. This alone cuts your storage costs by 60-80% for typical corporate mailboxes.
Gmail's threadId works only within Gmail. If your app shows a thread that spans a Gmail account and an Outlook account (e.g., a reply sent from a different mailbox), the native threading IDs are useless. Fix: build a cross-provider threading engine based on the Message-ID, In-Reply-To, and References headers. Normalize subject lines (strip Re:/Fwd: prefixes) as a fallback for mails missing these headers.
Delta sync relies on a stored cursor: Gmail's historyId, Microsoft Graph's deltaLink, IMAP's MODSEQ. If your sync worker restarts and the cursor is stored only in memory, you lose your position in the change stream. The next sync starts from scratch, duplicating all historical messages or missing the gap. Fix: persist the cursor to your database after every successful sync cycle, atomically with the last batch of processed changes.
For CRM use cases, you need both inbound (received) and outbound (sent) emails to build a full communication timeline. Gmail's INBOX label only covers received mail; you also need SENT. Microsoft Graph requires querying the SentItems folder separately. IMAP requires selecting the Sent folder explicitly. Fix: sync all relevant folders on account setup, not just INBOX, and map provider-specific folder names to normalized types in your data model.
Frequently Asked Questions about Email Sync APIs
The most common questions developers ask when implementing an email synchronization API for the first time.
historyId cursor with the users.history.list endpoint. Microsoft Graph uses a deltaLink returned by the /messages/delta endpoint. IMAP uses the MODSEQ value from the CONDSTORE extension. A unified email sync API normalizes these three different mechanisms behind one consistent interface.GET /emails with the account_id to retrieve synced messages, and register a webhook endpoint to receive real-time email.new events. Unipile handles OAuth, token refresh, delta sync, and webhook delivery automatically.gmail.readonly. If you need to mark messages as read or move them, request gmail.modify. For Microsoft Graph, request Mail.Read for read-only access or Mail.ReadWrite for full access. Always request the minimum scopes your application actually needs - Google's CASA verification (required for apps with 100+ users) reviews scope justification closely, and over-scoping can delay your approval.401 Unauthorized responses, use the stored refresh token to obtain a new access token, and retry the failed request transparently. Refresh tokens themselves can be revoked. When revocation is detected (invalid_grant error), mark the account as needing re-authorization and notify the user. Unipile handles all token lifecycle management automatically for linked accounts.EXISTS notifications when new messages arrive. This allows near real-time inbox sync (under 1 second latency) without constant polling. IDLE connections must be refreshed every 29 minutes to prevent server timeouts. IDLE works with any IMAP server that supports it, which includes most modern mail servers including corporate Exchange, Gmail via IMAP, and Outlook via IMAP.
Instagram