# How to ingest Gmail with email forwarding

Source: https://developer.nylas.com/docs/cookbook/agent-accounts/ingest-gmail-via-forwarding/

Gmail can forward incoming messages to another address automatically. If you point that forwarding at a Nylas Agent Account, the forwarded mail lands in a mailbox you read through the API. This lets you ingest a copy of a user's incoming Gmail without connecting the account over Google OAuth, which is useful when you only need new inbound mail and want to skip Google's restricted-scope verification.

An Agent Account is a Nylas-hosted mailbox with its own address and `grant_id`. It receives mail like any other mailbox and works with the standard messages endpoints and webhooks. Forwarding has clear boundaries worth knowing before you start: it captures mail only from the time you turn it on, it doesn't sync folders or labels, and the account can't send as the user's Gmail address. For full history or two-way sync, connect the account directly instead.

> **Info:** 
> **Prerequisites.** An API key, a registered domain (trial `*.nylas.email` subdomains work for prototyping, see [Setup domains](/docs/v3/agent-accounts/dns-provider-setup/)), and access to the Gmail account whose mail you want to ingest. To read mail in real time you also need a publicly reachable HTTPS endpoint for the `message.created` webhook. The commands below assume you've installed and authenticated the [Nylas CLI](https://cli.nylas.com/).

## How forwarding mail into an agent works

Gmail sends a copy of each incoming message to an address you nominate, and that address is an Agent Account mailbox you read through the API. You turn forwarding on once in Gmail, confirm a verification link, and from then on inbound mail arrives in the agent's inbox and fires a `message.created` webhook your backend handles within 1 to 2 seconds of delivery.

There are three ways to route Gmail into the agent address, in increasing order of selectivity:

- **Auto-forward everything.** Gmail's forwarding setting sends a copy of all new mail, except spam, to the agent address.
- **Forward by filter.** A Gmail filter forwards only messages matching a search (sender, subject, label) to the agent address.
- **Forward or BCC by hand.** The user forwards individual messages to the agent address to capture incoming mail, or adds the agent address as a BCC recipient when composing to capture a copy of outgoing mail.

The first two are persistent and run without the user in the loop once configured. The third is ad hoc and suits one-off capture or cases where you control the sending application.

## Create the Agent Account that receives the mail

The agent address is the destination Gmail forwards to, so create it first. Each Agent Account is a [grant](/docs/v3/auth/) on a domain you've registered, with 6 system folders provisioned automatically and a `grant_id` that works with every standard messages endpoint. Sends from the account are capped at 200 messages per day on the free plan, but that limit doesn't affect inbound, which is all this flow uses. The quickest path is the [Nylas CLI](https://cli.nylas.com/):

```bash
nylas agent account create gmail-ingest@agents.yourdomain.com
```

The command prints the new grant ID. Save it as `AGENT_GRANT_ID`. If you'd rather provision through the API, [`POST /v3/connect/custom`](/docs/reference/api/manage-grants/byo_auth/) creates the same account with the `nylas` provider:

```bash
curl --request POST \
  --url "https://api.us.nylas.com/v3/connect/custom" \
  --header "Authorization: Bearer <NYLAS_API_KEY>" \
  --header "Content-Type: application/json" \
  --data '{
    "provider": "nylas",
    "settings": {
      "email": "gmail-ingest@agents.yourdomain.com"
    }
  }'
```

For multi-tenant setups, provision one agent address per customer so each Gmail source maps to its own grant. See [Provisioning Agent Accounts](/docs/v3/agent-accounts/provisioning/) for that pattern.

## Turn on Gmail forwarding to the agent address

Forwarding is set in the Gmail account's own settings, so the user (or their Workspace admin) does this once. Gmail requires you to verify ownership of the destination before any mail flows. That matters here because the verification link arrives in the agent mailbox rather than a human inbox. Follow [Google's Gmail forwarding instructions](https://support.google.com/mail/answer/10957?hl=en), which break down to 7 steps:

1. In Gmail, open **Settings** then **See all settings**.
2. Open the **Forwarding and POP/IMAP** tab.
3. Click **Add a forwarding address** and enter `gmail-ingest@agents.yourdomain.com`.
4. Click **Next**, then **Proceed**, then **OK**. Gmail emails a verification link to the agent address.
5. Confirm that link (the next section shows how to do this through the API).
6. Back in **Forwarding and POP/IMAP**, select **Forward a copy of incoming mail to** and pick the agent address.
7. Choose whether Gmail keeps its own copy or archives it, then **Save Changes**.

Until step 5 is confirmed, forwarding stays inactive and no mail reaches the agent.

## Confirm the forwarding verification email from the agent inbox

When you add a forwarding address, Gmail sends the verification message to that destination, which is the Agent Account rather than the Gmail mailbox. It arrives from `forwarding-noreply@google.com` like any other inbound mail, so you read it through the agent's own grant without authenticating the Gmail account at all. Because Nylas-hosted grants don't support native search syntax, filter the message list with the structured `from` query parameter, which returns up to 50 messages per page by default. This request pulls the recent verification mail so you can read its confirmation link:

```bash
curl --request GET \
  --url "https://api.us.nylas.com/v3/grants/<AGENT_GRANT_ID>/messages?from=forwarding-noreply@google.com&limit=5" \
  --header "Authorization: Bearer <NYLAS_API_KEY>"
```

The response carries the message body. Gmail's verification mail contains both a numeric confirmation code and a clickable confirmation URL of the form `https://mail.google.com/mail/...&ui=...`. Visit that URL (a plain GET is enough) to activate forwarding:

```js
// Find the recent Google forwarding verification mail and confirm it.
const resp = await fetch(
  `https://api.us.nylas.com/v3/grants/${AGENT_GRANT_ID}/messages?from=forwarding-noreply@google.com&limit=5`,
  { headers: { Authorization: `Bearer ${NYLAS_API_KEY}` } },
);
const [verification] = (await resp.json()).data;

// Pull the confirmation link out of the HTML body and follow it.
const match = /https:\/\/mail\.google\.com\/mail\/[^"\s<>]+/.exec(verification.body);
if (match) await fetch(match[0]);
```

If you prefer not to script this, open the agent address in a mail client over [IMAP](/docs/v3/agent-accounts/mail-clients/) and click the link by hand. Either way, forwarding goes live once the link is confirmed.

## Forward only specific messages with Gmail filters

To ingest a subset of mail rather than the whole mailbox, use a Gmail filter. A filter forwards only the messages matching its criteria, such as receipts, a single sender, or messages carrying a label. Turn off the **Forward a copy of incoming mail** setting first, since a filter and a blanket rule conflict. Then build the filter in 4 steps:

1. In the Gmail search bar, click the filter icon and enter your criteria (for example `from:billing@vendor.com`).
2. Click **Create filter**.
3. Tick **Forward it to** and select the agent address.
4. Click **Create filter** to save it.

You can create several filters pointing at the same agent address, so one mailbox collects only the categories you care about while the user keeps the rest private. A filter acts only on mail that arrives after you save it, so it shares the same forward-only limitation as the blanket setting.

## Read forwarded messages through the API

Once forwarding is live, subscribe to `message.created` so your backend reacts to each forwarded message instead of polling. The trigger is identical in shape to `message.created` on any other grant, and Nylas fires it within 1 to 2 seconds of the mail arriving. Create the subscription from the CLI:

```bash
nylas webhook create \
  --url https://yourapp.example.com/webhooks/gmail-ingest \
  --triggers message.created
```

In the handler, acknowledge fast, confirm the message belongs to the agent grant, then fetch the full body. The webhook payload carries only summary fields, so a second call retrieves the complete message:

```js
app.post("/webhooks/gmail-ingest", async (req, res) => {
  // (Verify X-Nylas-Signature here, see /docs/v3/notifications/.)
  res.status(200).end();

  const event = req.body;
  if (event.type !== "message.created") return;

  const { grant_id, id: messageId } = event.data.object;
  if (grant_id !== AGENT_GRANT_ID) return;

  const resp = await fetch(
    `https://api.us.nylas.com/v3/grants/${AGENT_GRANT_ID}/messages/${messageId}`,
    { headers: { Authorization: `Bearer ${NYLAS_API_KEY}` } },
  );
  const message = (await resp.json()).data;

  // message.from carries the original sender on automatic forwarding.
  await ingest(message);
});
```

The `From` header behaves differently depending on how mail is forwarded. Gmail's automatic forwarding resends each message with the original sender preserved in `From`, so `message.from` is the person who wrote to the user. A manual forward rewrites `From` to the forwarder and nests the original headers in the body, so you parse the real sender out of the text. Test both paths against your own setup so your ingestion handles whichever the customer uses.

## Things to know about Gmail forwarding

A few constraints affect how you design around this approach:

- **Spam isn't forwarded.** Gmail drops spam from forwarding, so anything its filters flag, including false positives, won't reach the agent. Don't assume forwarding delivers every message a user receives.
- **Workspace admins can disable it.** Google Workspace administrators can turn off automatic forwarding for a whole organization. If your customer is on Workspace and forwarding won't save, the admin policy is the first thing to check.
- **There's no API to enable forwarding.** A user or their admin sets this up in Gmail's own settings. You can't provision it through Nylas or the Gmail API, so onboarding includes a manual step to document for customers.
- **No backfill.** Forwarding starts from the time you enable it. Existing mail in the user's mailbox stays invisible to it. For history or full two-way sync, a direct Google connection is the right tool.
- **Receive-only.** The agent reads forwarded mail but can't reply as the user's Gmail address. A reply leaves as the agent's own address, which breaks the thread from the user's side. Use a full grant if the agent needs to answer in the user's name.
- **Watch for loops.** Don't forward the agent address back into the source Gmail account. Pair a [policy](/docs/v3/agent-accounts/policies-rules-lists/) with a sender allowlist to keep the inbox to the domains you expect.
- **Retention applies.** On the free plan the agent inbox keeps mail for 30 days. Ingest into your own store rather than treating the agent mailbox as a permanent archive.

## What's next

- [Handle email replies in an agent loop](/docs/cookbook/agent-accounts/handle-replies/) for reacting to forwarded threads
- [Build a multi-turn email conversation](/docs/cookbook/agent-accounts/multi-turn-conversations/) if the agent needs to carry context across messages
- [Policies, Rules, and Lists](/docs/v3/agent-accounts/policies-rules-lists/) to filter forwarded mail before it reaches your code
- [Agent Accounts overview](/docs/v3/agent-accounts/) for the full picture of what an agent mailbox can do
- [Mail client access (IMAP & SMTP)](/docs/v3/agent-accounts/mail-clients/) to inspect the agent inbox directly