Email

Email in your product: send, sync, or both

Aug 25, 2026 · 3 min read

"Add email to the product" is three different projects that happen to share a name. Sending from the user's address, reading their inbox for context, and full two-way sync are separate commitments with separate costs. Teams that skip the choice end up building all three by accident, in the wrong order.

Depth one: sending as the user

The shallowest useful integration sends mail from the user's own address through their connected Google or Microsoft account. The message comes from a real person, lands in the real thread, and rides the provider's sending reputation instead of yours. For a CRM follow-up or a scheduling confirmation, that is the entire feature.

The complexity is smaller than the other depths, not small. You hold OAuth tokens now, which makes you a credential store with everything that implies. You have to thread replies correctly, which means tracking per-provider message and thread identifiers rather than just firing off new messages. And you have to handle the send that fails at 2 a.m. because a token was revoked, without silently dropping it.

Depth two: one-way sync for context

The second depth reads the inbox and shows relevant messages inside your product: the email thread next to the deal or the support ticket. Nothing is written back. Users stop switching to their mail client to answer "what did they say?", and your product becomes the place where the whole story is visible.

The cost is a sync engine. You need cursors so you fetch only what changed, storage for message data you are now responsible for, a matching layer that connects messages to your records, and a retention answer, because you hold copies of other people's correspondence from the first sync onward. Read-only does not mean low-stakes.

Depth three: full two-way sync

Full two-way sync makes your product a peer of the mail client. Users read, reply, and archive in your interface, and everything stays consistent with the provider. Drafts sync. Read state syncs. A message sent from a phone on a train shows up in your app seconds later.

This is a step change, not an increment over depth two. You reconcile state in both directions, resolve conflicts when both sides changed the same thing, and absorb the fact that Google and Microsoft model threading, folders, and labels differently enough that your abstraction has to take a position. Budget for it like a sync product, because that is what you are building.

Pick the depth the workflow needs

Most products should stop at depth one or two. Sending plus one-way context covers the large majority of "email in the product" requests, and both can ship in weeks. Full two-way sync is only right when your product genuinely replaces the mail client for a specific workflow, and you should be able to name that workflow before you commit.

  • If users only need mail to go out under their name, build sending. Threading and failure handling are the hard parts, and both are tractable.
  • If users keep asking what the customer said, add one-way sync. Context is the value; write access adds risk without adding much on top.
  • If users would abandon their inbox for your product during a workflow you can name, build two-way. Anything less and the sync engine costs more than the feature returns.

One surface, three depths

Whichever depth you choose, code against one interface so going deeper later is an extension rather than a rewrite. That argues for normalizing over Google and Microsoft from the first day, even if only one provider matters right now. Horato exposes sending, reading, and sync through one API across both, which turns the depth question back into what it should be: a product decision, not a second integration project.