Engineering reliability
Gmail OAuth vs. a dedicated inbox for AI agents
Choose Gmail OAuth when an agent must work inside a user's mailbox, and a dedicated inbox when the workflow should own a narrow email identity.
Use Gmail OAuth when an AI agent must act inside a person's existing mailbox, with that person's history, labels, and sending identity. Use a dedicated agent inbox when the workflow should own a separate address and should not inherit access to years of human email. Many production systems need both, but each message should have one clear owner.
The choice changes more than authentication. Gmail integration means consent, token storage, mailbox synchronization, and Google Cloud Pub/Sub. A dedicated inbox moves those jobs behind an email API, but it creates a separate identity that users will not automatically see in their usual mailbox.
The short decision table#
| Requirement | Gmail OAuth | Dedicated agent inbox |
|---|---|---|
| Read a user's existing email and history | Good fit | Wrong source of data |
| Send as the user's established Gmail address | Good fit | Uses a separate agent address or configured domain |
| Give every workflow its own disposable address | Requires account or alias administration | Good fit |
| Avoid access to unrelated human conversations | Harder because mailbox scopes cover a user's account | Natural boundary if the inbox is workflow-specific |
| Receive changes without polling | Gmail watch through Cloud Pub/Sub, followed by history sync | Provider webhook or a bounded wait tool |
| Let a user revoke access from Google | Native OAuth consent and revocation | Revoke the email API key or mailbox access |
| Keep mail visible in the user's normal Gmail UI | Native | Requires forwarding, copying, or a separate operator view |
| Provision mailboxes from an agent workflow | Not the normal OAuth model | Designed for machine-owned mailboxes |
Neither column is inherently secure. A Gmail reader with a narrow purpose can still hold a broad read scope. A dedicated inbox can still accept hostile email from anyone who knows its address. The better design is the one whose authorization boundary matches the job.
What a Gmail integration actually includes#
Gmail OAuth starts with user consent and the narrowest scope that supports the workflow. Google's current scope list classifies gmail.send as sensitive. Scopes that read message bodies, including gmail.readonly and gmail.modify, are restricted. A public app that requests user data may need OAuth verification, and storing or transmitting restricted-scope data on a server can require a security assessment under Google's policy. Check the current rules and exemptions for your application before committing to the integration.
The event path is also more involved than a webhook carrying a complete email. To watch an inbox, your service creates a Google Cloud Pub/Sub topic, grants Gmail permission to publish to it, and calls users.watch:
POST https://gmail.googleapis.com/gmail/v1/users/me/watch
Authorization: Bearer ya29...
Content-Type: application/json
{
"topicName": "projects/acme-agent/topics/gmail-updates",
"labelIds": ["INBOX"],
"labelFilterBehavior": "INCLUDE"
}
The response contains a current historyId and an expiration timestamp:
{
"historyId": "9876543210",
"expiration": "1788217200000"
}
Google requires a new watch call at least every seven days and recommends renewing daily. The Pub/Sub notification contains the Gmail address and a newer history ID, not the changed message body. Your worker calls history.list, pages through changes, fetches the messages it needs, and saves the latest processed history ID.
That cursor needs a recovery plan. Google notes that notifications can be delayed or dropped. If a stored startHistoryId falls outside the available history range, history.list returns 404 and the client must run a full sync. A reliable Gmail connector therefore needs all of these moving parts:
- Encrypted access and refresh-token storage.
- Consent renewal and revocation handling.
- Pub/Sub topic, subscription, and acknowledgement logic.
- Daily watch renewal before
expiration. - Durable history cursors and idempotent message processing.
- Periodic reconciliation when no notification arrives.
- A full-sync path for an expired history cursor.
This work is justified when the product promise is "work in my inbox." It is unnecessary baggage when the agent only needs an address for order confirmations, support requests, or verification messages.
What changes with a dedicated inbox#
A dedicated inbox is an application resource. The workflow owns the address, and messages arrive there because a person or system intentionally sends to that address. The agent does not need permission to read a founder's receipts, a support manager's private threads, or any other conversation outside the workflow.
Postfleet provisions a mailbox in one request:
curl -X POST https://api.postfleet.ai/api/v1/mailboxes \
-H "Authorization: Bearer $POSTFLEET_KEY" \
-H "Content-Type: application/json" \
-d '{"slug":"returns"}'
A successful request returns the mailbox ID and its address:
{
"id": "b_8c2f...",
"address": "agent-returns@mail.postfleet.ai"
}
Use an account-wide or bootstrap key for provisioning, then create a different working key bound to the new mailbox. A receive-only agent should have can_read enabled and can_send disabled. The provisioning credential does not belong in the worker.
Inbound mail can reach your application through a signed message.received webhook. The payload contains Postfleet's cleaned body, comprehension status, and any configured schema extraction. Delivery is at least once, so the consumer verifies the signature and deduplicates the stable event_id. The webhook contract has the signing and retry details.
An MCP workflow can instead use wait_for_email with a bounded timeout and sender or subject filters. The secure MCP inbox guide shows a receive-only setup with three read tools and a mailbox-bound key.
The dedicated model removes Gmail consent and synchronization from your code. It does not recreate Gmail features. There is no preexisting user history to search, no Gmail label state to preserve, and no promise that mail appears in a person's normal Gmail interface. Those are benefits only when the workflow does not need them.
Compare the blast radius, not the login screens#
OAuth is often described as safer because the user grants access, while API keys are described as simpler but broader. That comparison misses the resource each credential can reach.
A token with gmail.readonly can read the connected user's messages and settings. That may be exactly what an assistant needs, but it also means one malicious email sits beside years of valuable correspondence. Prompt injection does not need to steal the token to cause damage. It can try to persuade the agent to search other threads and disclose what it finds.
There is a second difference that survives whichever credential you pick: whether anything screens the message before the model reads it. A raw mailbox hands over the sender's bytes as they arrived, headers and hidden HTML included. The gates a message passes first are what turn that into a body with a status attached.
A Postfleet key can be bound to one mailbox and restricted to read or send. A receive-only returns agent has no server-authorized path to a payroll mailbox or a send operation. Its separate address reduces the amount of unrelated data available after a bad model decision.
This is not a reason to replace every Gmail connection. It is a reason to avoid connecting a human mailbox when the task never required one. Email prompt injection explains why narrowing both the untrusted source and the dangerous tool matters more than trusting a classifier to catch every attack.
When Gmail OAuth is the right answer#
Choose Gmail OAuth when the workflow depends on the user's mailbox as a product feature. Common examples include:
- Finding messages the user already received.
- Drafting or sending as the user's Gmail identity.
- Applying labels or changing read state.
- Working with an existing thread that should stay in Gmail.
- Honoring user-controlled consent and revocation for a multi-user application.
Ask for the smallest scope that supports those actions. A sender that never reads should not request gmail.modify. A metadata-only workflow should not request body access. Keep refresh tokens out of model context and use a server-side policy layer to validate every Gmail operation.
Gmail push is a change signal, not a complete delivery record. Store history cursors per user, make message processing idempotent, renew watches before they expire, and schedule reconciliation. The Gmail synchronization guide is more important to production reliability than the first OAuth callback.
When a dedicated inbox is the better boundary#
Use a dedicated inbox when email belongs to the workflow rather than a person. It works well for:
- Account verification and passwordless login messages.
- Vendor invoices sent to an extraction pipeline.
- Support or returns addresses processed by one agent team.
- Test accounts that need a real address during automated runs.
- Intake forms where the sender is expected to email the application itself.
Give each workflow a mailbox that can be revoked without affecting a person's email account. Bind the working key to that mailbox. If the agent only extracts data, turn off send. If it sends external replies, add recipient policy and human approval according to the risk.
Postfleet's quickstart covers provisioning, REST, webhooks, and MCP. Authentication and key scoping covers the control-plane split and mailbox binding.
A hybrid design without ambiguous ownership#
Some products genuinely need both models. A sales assistant might read a user's Gmail history but use a dedicated intake address for website leads. A support system might keep the public queue in a dedicated inbox and create a Gmail draft only when a human agent takes over.
The integration stays understandable when the application records where each message came from and which identity owns any reply. Do not copy an email into both systems and let two workers race to answer it. Use one durable message record, one action owner, and an idempotency key for each outbound reply.
Also decide where an operator investigates failures. Gmail sync failures belong with OAuth and cursor health. Dedicated inbox failures belong with webhook delivery and mailbox policy. A single dashboard can show both, but combining their retry loops creates duplicate sends and confusing audit trails.
Questions to settle before building#
Write down the answers before choosing an integration:
- Must the agent read email that already exists in a user's account?
- Must replies come from the user's Gmail address?
- What unrelated messages become readable if the credential is compromised or misused?
- Who owns consent, token revocation, and offboarding?
- Can the workflow use a separate address without confusing customers?
- Who renews watches, repairs cursor gaps, and handles full sync?
- Does a human need every message and draft in the Gmail interface?
- Can one mailbox and one restricted key represent the workflow more accurately?
If the first two answers are yes, Gmail OAuth is probably part of the architecture. If they are no, a dedicated inbox is usually the smaller system and the smaller trust boundary. The hybrid case should be an intentional split, not a migration left half-finished.