
Real-Time Gmail Integrations at Scale: Workspace Delegation, Pub/Sub, and Migrating Off Homebrew
How to build real-time Gmail integrations with Workspace Delegation, Pub/Sub, and scalable migration patterns from polling architectures

Chris Lopez
Founding GTM
Real-Time Gmail Integrations at Scale: Workspace Delegation, Pub/Sub, and Migrating Off Homebrew
If your product reads or writes email on behalf of customers, you have probably built a Gmail integration in-house at some point. Most teams do. The Gmail API surface is well-documented, OAuth is familiar, and the first version is easy to bring up against a single test inbox. The trouble starts when the integration needs to run in production for hundreds of customers, ingest mail in real time without saturating quota, ride along on existing service-account auth that already has Google Workspace Delegation, and gracefully replace whatever homebrew polling code your team shipped two years ago when the use case was simpler.
This post is a practitioner's walkthrough of the patterns that actually work for real-time Gmail integrations: how to combine Gmail's Pub/Sub-based push notifications with native product integrations infrastructure, when to use Google Workspace Delegation versus per-user OAuth, why proxy actions are the cheapest way to migrate off a homebrew polling layer, and where filtering, schema mappings, and oversize-message handling tend to bite teams late. The patterns are drawn from work with engineering teams across SaaS, vertical AI, sales tooling, and revenue platforms, where Gmail-as-a-data-source is foundational to the product.
We will get specific. Service account JSON, Pub/Sub topic permissions, the trade-off between subscribe actions and scheduled reads, when to use proxy.withampersand.com instead of gmail.googleapis.com, how to handle the oversized-message signed URL pattern, and the YAML shape of an integration definition that gets you from zero to "real-time email is flowing into my product" in a single afternoon.
Why Gmail integrations look easy and turn out hard
Gmail's REST API is one of the cleanest in the industry. There are well-defined endpoints for messages, threads, labels, and history. OAuth 2.0 works. The pagination story is well-defined. Push notifications are documented. By all measures, this should be a solved problem.
It is not. The reason is that Gmail integrations live at the intersection of three hard problems that most teams underestimate.
The first is auth. Gmail authentication has at least three meaningfully different patterns, each suitable for different deployment contexts: per-user OAuth, where each end user grants your app access to their own mailbox; Google Workspace Delegation, where a service account in the customer's Workspace tenant is granted domain-wide delegation and impersonates users; and service account auth without delegation, suitable only for resources owned by the service account itself. Each comes with different scopes, different audit trails, different security posture for the customer's IT team, and different operational characteristics. Choosing wrong is expensive to fix later, and the right choice is usually dictated by what the customer's IT and Security teams will sign off on, not by what is easiest to implement.
The second is real-time sync. Polling Gmail every five minutes per customer at scale is operationally expensive and adds latency that compounds against any product whose value depends on reacting fast to incoming email. Real-time sync via Gmail's push notifications uses Google Cloud Pub/Sub as the delivery mechanism, which means you have to set up a Pub/Sub topic, grant Gmail permission to publish to it, subscribe your application to the topic, and handle the message-ack lifecycle correctly. None of those steps are individually hard, but together they require a working knowledge of GCP IAM, Pub/Sub semantics, and Gmail's history-based event model.
The third is data shape. Gmail messages are big. They contain headers, bodies in multiple MIME parts, attachments, and metadata that often exceeds reasonable webhook payload sizes. Real-time integrations have to handle the case where a message is too large to deliver inline and gracefully fall back to a signed URL pattern. Most homebrew implementations ignore this until the day a customer's enterprise-attached PDF crashes the pipeline.
We have seen all three of these problems in work with engineering teams replacing homebrew Gmail integrations. The patterns below are what survives in production.
The auth choice: when to use Google Workspace Delegation
The first decision is which auth flow to support. Per-user OAuth is the default for self-serve products: each user installs the integration, grants scopes, and gets a refresh token. This is fine for individual users in small teams, but it falls apart in enterprise contexts where IT does not want individual users granting third-party access on behalf of the company.
Google Workspace Delegation is the answer in that case. The customer's IT admin creates a service account, grants it domain-wide delegation, and authorizes specific OAuth scopes for it. Your integration then impersonates specific users in that Workspace tenant, with the impersonation scope controlled by the IT admin. From a Security review standpoint, this is much easier to approve, because the access is centrally managed and revocable in one place rather than spread across hundreds of individual user grants.
The technical setup is more involved. The customer creates the service account in their GCP project, downloads a JSON key, and shares it with your app (typically via a secrets manager). Your app base64-encodes the key and configures it as the credential for the integration. You then specify the list of email addresses your integration is allowed to impersonate, plus the scopes you need. Each call to the Gmail API is made on behalf of one of those impersonated users, and the audit trail in the customer's Workspace shows the service account as the actor.
Two practical points come up in almost every implementation. First, the list of impersonated users is usually a moving target. Customers add and remove employees, and your integration cannot impersonate a user who is not on the list. Most homebrew implementations end up with a custom job that syncs the impersonation list from the customer's HRIS or directory, which adds another moving piece. Second, customers who want to scope down the impersonation to specific opted-in users (for compliance reasons) need to manage the list dynamically, which usually means surfacing the list through your app's admin UI.
Per-user OAuth and Workspace Delegation can coexist in the same integration. The right model is to make the auth choice a per-tenant configuration: small teams using per-user OAuth, enterprise customers using Workspace Delegation. The thing you do not want is to pick one and discover six months in that the other half of your prospects can't deploy your integration because the auth model does not match their IT posture.
Real-time sync: Pub/Sub, subscribe actions, and history events
Gmail's push notification model is built on Google Cloud Pub/Sub. The flow is as follows: your app calls users.watch on the Gmail API for a given user, registering a Pub/Sub topic where Gmail will publish notifications when the user's mailbox changes. When a new message arrives (or any other history event happens), Gmail publishes a small notification to the topic containing the user's email address and the latest history ID. Your app then calls users.history.list against that history ID to get the actual list of changed messages, then fetches each message individually if it needs the body.
The right way to build this in 2026 is to abstract the Pub/Sub plumbing behind a subscribe action. Subscribe actions in Ampersand let you declare, in a YAML config, that you want real-time notifications for new Gmail messages, and we handle the Pub/Sub topic, the Gmail watch lifecycle, the history-list translation, and the destination delivery. Your app receives a webhook (or other destination) per new email, and you do not have to operate any of the Pub/Sub or watch-renewal infrastructure yourself.
The shape of the YAML looks something like this for a Gmail integration that ingests messages and labels in real time:
specVersion: 1.0.0
integrations:
- name: read-write-google-gmail
displayName: Gmail
provider: google
read:
objects:
- objectName: message
schedule: 30m
backfill:
defaultPeriod:
days: 2
- objectName: label
subscribe:
objects:
- objectName: message
inheritFieldsFrom: read.objects.message
createEvent: true
updateEvent: true
deleteEvent: true
The interesting choices are: a backfill of two days when the integration is first installed, ensuring that when a customer signs up they immediately have history rather than only seeing email from the moment they connected; a 30-minute scheduled read for the message object as a fallback to catch anything missed by the subscribe path; and explicit subscribe events for create, update, and delete, so the consuming app can keep its local copy of mailbox state in sync.
For most teams, the right path is to start with subscribe actions for real-time and use scheduled reads as a backstop. Subscribing only is risky: if the watch expires (Gmail watches need to be renewed weekly) and your app does not detect that, you can silently miss email. A scheduled read on a 30-minute or hourly cadence catches anything dropped by the real-time path.
Migrating off a homebrew polling integration: the proxy pattern
A common starting point is a homebrew Gmail integration that already polls. The team has invested in their own polling logic, has handlers for batches of messages, has retry semantics, and may have customer-specific filtering. Telling them to throw all that away and rewrite against subscribe actions is a non-starter for most engineering teams in week one. They want incremental migration.
Proxy actions are designed for exactly this case. Instead of calling gmail.googleapis.com directly, the team's existing polling code calls proxy.withampersand.com with the same path and body. We handle authentication transparently (the call is signed using the customer's stored OAuth or Workspace Delegation credential), and we optionally rate-limit on the team's behalf with a header (x-amp-rate-limiter-mode) that throttles calls before Gmail does and returns Retry-After hints when needed.
The migration looks like this: the team replaces the Gmail API hostname in their polling code with the Ampersand proxy URL, and the rest of the code stays the same. They get managed authentication, rate-limit handling, and observability for free. Then, at a later stage when they have time, they migrate the polling loop to subscribe actions and turn the polling off entirely. The proxy is a stepping stone, not a destination.
This pattern matters because it lets engineering teams adopt Native Product Integrations without doing a big-bang rewrite. We have written about the broader argument for migrating from embedded iPaaS to Native Product Integrations, and the same logic applies to migrating from homebrew. The win is not "rewrite everything." The win is "stop maintaining the integration plumbing yourself, even while you keep your existing application logic running."
Filtering, oversize messages, and the long tail
Three production gotchas tend to surface in real-time Gmail integrations after the happy path is working.
The first is filtering. In many products, you do not want to ingest internal email (employees emailing each other inside the company), only external email (customer-facing threads). Gmail does not natively support this kind of filter at the subscribe layer; you either ingest everything and filter on your side, or you build a custom rule on top of Gmail labels. The native product integrations approach is to support filter-by-field-value at the read action layer, where you can declare that you only want messages matching certain conditions. For Gmail specifically, the most reliable filter is "does any participant have a domain that is not the customer's domain," but that requires the integration to know the customer's domain at install time. Worth noting up front.
The second is oversize messages. Gmail messages can include attachments that push the payload size over what most webhook destinations can accept. Subscribe actions handle this by switching to a signed URL pattern when a message exceeds the size threshold: the destination receives a webhook containing a URL, which the consuming app can GET to retrieve the full message. This is invisible to the consumer in the happy path and critical to handle correctly in the edge case. Most homebrew implementations do not handle this and end up with silent message drops at the high end of the distribution.
The third is field mapping and schema drift. Gmail's schema is mostly fixed, which is the easy case. The same machinery that handles Gmail messages also handles dynamic-schema providers like Salesforce, HubSpot, and NetSuite, where customers add custom fields constantly. We covered the deeper version of this problem in field mapping is how AI agents learn enterprise reality, but the principle applies even in the Gmail case: your integration definition should declare what fields you care about, and the platform should handle the case where the underlying provider's schema changes.
Calendar comes along for free
A practical note: most products that integrate with Gmail also need to integrate with Google Calendar, and most teams build that as a separate integration. The clean approach is to define both as separate integration modules that share the same Google connection. The customer authenticates once (per-user OAuth or Workspace Delegation) and your app gets both Gmail and Calendar access from a single auth grant.
In a YAML model, this looks like two integration entries under the same provider:
specVersion: 1.0.0
integrations:
- name: read-write-google-gmail
provider: google
read:
objects:
- objectName: message
subscribe:
objects:
- objectName: message
- name: read-write-google-calendar
provider: google
read:
objects:
- objectName: event
subscribe:
objects:
- objectName: event
The auth grant is shared, the deployment is one operation, and the customer experiences a single install flow that lights up both integrations. This is one of the design choices that makes a real difference in customer onboarding. Teams who build Gmail and Calendar separately end up with two install flows, two consent dialogs, and two opportunities for the customer to drop off in the middle.
Comparison: homebrew Gmail integration versus Ampersand-managed
| Concern | Homebrew Gmail Integration | Ampersand Native Product Integration |
|---|---|---|
| Initial implementation time | 1 to 2 weeks for first version | 1 afternoon |
| OAuth and refresh handling | Build per-tenant, store securely, rotate | Managed |
| Workspace Delegation support | Custom service-account handling | Built in, single config change |
| Real-time via Pub/Sub | Provision topic, manage IAM, handle watch renewal | Subscribe action, declarative |
| Polling fallback | Custom scheduler | Scheduled read in YAML |
| Rate limiting and Retry-After | Per-call logic | Centralized via proxy or scheduler |
| Oversized message handling | Custom signed URL fallback | Built in |
| Multi-tenant credential storage | Build with secrets manager | Managed |
| Migrating from polling to push | Incremental rewrite | Proxy first, subscribe later |
| Adding Calendar (or Drive, or Contacts) | New integration project | Same connection, new YAML module |
| Engineering cost in year 2 | High and growing | Low and flat |
| Audit logs and observability | Build with your stack | Built in |
The first row is misleading. The interesting row is the last one: year-two cost. Homebrew Gmail integrations don't break dramatically; they break gradually, in ways that erode trust over time. A Pub/Sub permission expires. A watch is not renewed. A new attachment type breaks parsing. A customer's IT admin rotates the service account key. Each of these is a small fire, individually fixable, but cumulatively they are an integration team's Tuesday afternoon for the rest of the year.
How Ampersand handles this
Ampersand is a deep integration infrastructure platform for product developers. Native Product Integrations means your team writes a YAML file describing the integration (provider, objects, sync strategy, write actions, scopes), and Ampersand handles the operational plumbing: managed authentication with automatic token refresh, multi-tenant credential storage, Pub/Sub setup for real-time sync, scheduled reads with backfill, bulk write optimization, on-demand read and write API endpoints, custom objects and dynamic field mapping, dashboards with logs, alerting, error handling, and quota management.
For Gmail specifically, that means the Pub/Sub topic, the watch lifecycle, the history-list translation, the oversize signed URL fallback, the auth refresh, and the destination delivery are all things you do not write. You declare what you want and we run it.
The platform supports hundreds of systems of record (NetSuite, SAP, Sage, Salesforce, HubSpot, Marketo, Microsoft Dynamics 365, Zendesk, Gong, and many more via open-source connectors), is GDPR compliant, and is ISO certified. Customers like 11x have used the platform to bring AI phone agent latency down dramatically. Muizz Matemilola, on the engineering team there, put it as: "Using Ampersand, we cut our AI phone agent's response time from 60 seconds to 5." For engineering teams replacing homebrew integrations, the testimonial that resonates more is from John Pena, CTO at Hatch (a Yelp company): "Ampersand lets our team focus on building product instead of maintaining integrations. We went from months of maintenance headaches to just not thinking about it." Both quotes describe the same underlying win: integration plumbing stops being a thing your team operates.
The platform is integration-as-code, version-controllable, and CI/CD-friendly, which matters as your integration matrix grows. Adding the next provider is another YAML file in the same repository as the first, deployed through the same pipeline.
The Ampersand sell
If your team is building or maintaining Gmail integrations in production, the math gets straightforward. Subscribe actions plus Workspace Delegation plus the proxy gives you everything you need to ship real-time Gmail in a day, replace homebrew code incrementally, and stop operating Pub/Sub, watch renewals, and oversize-message handling yourself. You write one YAML file. We run the integration. When you want to add Calendar, Drive, or any of the other Workspace surfaces, you add another module under the same connection, and your customers do not get a second consent dialog.
The same platform handles the deeper integrations you will need for CRMs and ERPs as your product grows: bi-directional read and write to Salesforce, HubSpot, NetSuite, Microsoft Dynamics 365, SAP, Sage, and the rest. For teams scaling integration breadth and depth simultaneously, the leverage compounds. The Ampersand documentation walks through the full data model, the subscribe action shape, the proxy pattern, and the auth providers in detail. The how it works page gives a developer-experience tour of the platform end to end. If you want to read more about the broader argument, building multi-tenant CRM integrations at scale is the deeper version of this story for CRM-shaped providers.
FAQ
When should I use Google Workspace Delegation versus per-user OAuth?
Use per-user OAuth for self-serve products where individual users install integrations themselves. Use Google Workspace Delegation when selling into enterprise where IT and Security want centralized control over third-party access. The right answer for many products is to support both: small customers get per-user OAuth, large customers get Workspace Delegation. The auth flow is a per-tenant configuration, not a global product decision. Most teams start with per-user OAuth and add Workspace Delegation when their first enterprise prospect's IT team raises it as a requirement.
Why is real-time sync via Pub/Sub better than polling?
Three reasons. Latency is dramatically lower (milliseconds versus minutes). Operational cost is lower because you are only paying for actual events, not every polling cycle that returns nothing. And you avoid the rate-limit pressure that polling thousands of mailboxes at the same interval creates. The cost is implementation complexity: you have to provision a Pub/Sub topic, grant Gmail permission to publish, manage watch renewal, and handle the history-list pattern. Subscribe actions abstract all of that, which is why this is a place where Native Product Integrations infrastructure pays off quickly.
What happens if a Gmail watch expires?
Gmail watches expire after seven days unless renewed. If your integration does not renew the watch (and many homebrew implementations do not), the push notifications silently stop. Your app stops getting new email events without any error. This is one of the most common failure modes for homebrew Gmail integrations. The platform-managed approach handles renewal automatically and surfaces an alert if the renewal fails, so you do not silently drift into a degraded state.
How do I handle email messages that are too large to deliver in a webhook?
Subscribe actions handle this by automatically detecting when a message exceeds the destination size threshold and switching to a signed URL pattern. The webhook payload contains a URL that the consuming app retrieves with a GET request to read the full message. This is documented in the destinations section of the Ampersand documentation, and the consuming code only needs to handle the case where the message field is replaced with a URL pointer.
Can I migrate from a homebrew polling integration without rewriting everything at once?
Yes, this is the proxy action pattern. Replace the Gmail API hostname in your existing polling code with proxy.withampersand.com, configure the integration in a YAML file, and your existing code keeps working with managed authentication and rate-limit handling. Then, when you have time, migrate the polling loop to subscribe actions for real-time delivery and turn the polling off. The proxy is the stepping stone that makes the migration incremental.
Does Ampersand store my email data?
The proxy is a passthrough. Subscribe action data goes from Gmail to your destination directly, without being stored on Ampersand infrastructure (other than transient handling during delivery). The platform is designed to be stateless on the data plane, which matters for data residency, retention, and compliance reviews. The Ampersand documentation covers the data flow in detail.
How does this scale to other Google Workspace surfaces?
Calendar, Drive, Contacts, and the other Workspace APIs follow the same pattern. Each is a separate integration module under the same Google connection, sharing the same auth grant. Your customer goes through one consent flow and your app gets all the surfaces you declared. Adding a new surface is a YAML change, not a new engineering project.
Conclusion
Real-time Gmail integration is one of those workloads that looks straightforward and quietly turns into a long-running engineering responsibility. Auth has to handle both per-user OAuth and Google Workspace Delegation. Real-time sync requires Pub/Sub topic provisioning, watch renewal, and history-list translation. Oversize messages need a signed URL fallback. Filtering for external-only email requires custom logic. And every one of those pieces has to keep working, per tenant, year over year, while your team is trying to ship product.
Native Product Integrations infrastructure is what lets a team treat Gmail as a declarative dependency rather than an operational responsibility. Subscribe actions plus the proxy plus Workspace Delegation give you a path from "we have homebrew polling code" to "we have real-time Gmail at scale" without a big-bang rewrite. And the same platform handles every other system of record your product will eventually need to touch.
If your team is in the thick of building or maintaining a Gmail integration and the next month of work feels like maintenance instead of product, the Ampersand site is the place to start, the documentation shows the YAML data model and the subscribe action shape, and the how it works page walks through the developer experience end to end. The first integration is the one you write. Every one after that should be the one you run.