# Gmail history sync

Gmail keeps a change log, and each position in it has a history ID. Connect stores the last one it processed and asks Gmail what changed since — so catching up after an hour, or after a day, costs the changes rather than the mailbox. When the stored position is too old for Gmail to answer from, the incremental question can no longer be asked and a new starting point has to be established.

- **Status:** Available
- **Audience:** both
- **Channels:** email
- **In the app:** #/mailboxes, #/inbox
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/email/gmail-history-sync/

## Why a change log beats a list

The naive way to keep a mailbox in step is to list its messages and compare with what you already have. That cost grows with the size of the mailbox and is paid on every pass, forever, whether or not anything happened. On a long-lived business account it is the difference between a sync that is free when nothing arrived and one that is expensive at three in the morning because the mailbox is large.

Gmail's history is a log of changes with monotonically increasing identifiers. Ask it what has happened since a given ID and it answers with the changes, not the mailbox. Connect records the last ID it successfully processed for that mailbox and uses it as the question for the next pass. That is the whole mechanism, and the reason `gmail_history_sync` is a named capability rather than an implementation detail.

> **Note** The Gmail API itself is described in the [technology reference](/docs/technology/). This page is about what Connect does with the history mechanism, in particular around failure.

## When the position advances

| Outcome of the pass | Position | Consequence |
|---|---|---|
| Changes fetched and processed | Advances to the newest processed ID | Next pass asks for what happened after them |
| Nothing changed | Unchanged | A [quiet mailbox](/docs/email/quiet-mailbox/); nothing to record |
| The daily allowance refused the work | Does **not** advance | The changes are still pending at Gmail and will be asked for again; Needs You shows the refusal |
| The pass failed | Does not advance | The same question is asked next time; a health verdict names the failure |
| Gmail cannot answer from the stored ID | Cannot be used | A new starting point has to be established — see below |

Only the first row advances anything. That is the discipline the whole mechanism depends on: a position is a claim that everything up to it has been dealt with, and advancing it past work that was refused or failed converts a delay into a permanent loss. [How a mail cursor loses messages, and how to stop it](/research/cursor-loss/) sets out the failure in general terms; the email channel's answer to it is this rule.

## A gap in the history

A history ID is only useful while Gmail can still answer questions from it. A position that has fallen out of the usable range is not an error in your mailbox and not something Connect can negotiate — the provider simply cannot say what changed since a point it no longer holds. The window is Gmail's property, not a Connect setting, and it is documented by Google rather than here.

When that happens there is exactly one thing that can be done: stop asking the incremental question and establish a new position, the way a newly connected mailbox does on its first pass. What that recovers is what Gmail offers at that moment; what it cannot recover is a change nobody can still describe.

> **Careful** This is the argument against long unplanned outages on a busy mailbox, and the argument for noticing a mailbox that has stopped fetching rather than discovering it a fortnight later. A mailbox whose position has gone stale still works; it has simply lost the cheap way to catch up.

## What history sync does not do

- It does not decide what is answered. Triage, autonomy and the allowance are separate and unchanged.
- It does not apply to other providers. IMAP resumes by UID — [IMAP UID and cursor lifecycle](/docs/email/imap-cursor/) — and a Microsoft mailbox by the Graph adapter's own position.
- It does not affect canonical records. Everything fetched still crosses the bridge, and the engine still reads only `threads`, `messages` and `contacts`.
- It does not make a burst smaller. A thousand changes since the last position is still a thousand changes — [Large batches and bursts](/docs/email/burst-handling/) covers what happens then.

Nor is it visible in the product as a thing to manage. There is no history ID to copy, reset or type in. The reason to understand it is diagnostic: it explains why a mailbox that was disconnected briefly catches up instantly, and why one that was disconnected for a long time behaves as though it were newly connected.

## Questions

### Does a long outage mean lost email?

Not in the sense of mail being deleted — the messages are still in Gmail. What can be lost is the cheap incremental path to them, in which case a new starting point is established from what Gmail offers at that moment.

### Can I force a full re-read?

There is no history ID to reset by hand. What re-establishes a starting point is a fresh connection, which is why reconnecting is sometimes suggested — and why it should not be the first move when the real symptom is a mailbox that is merely quiet.

### Why did a refused batch not move the position?

By design. A refusal means the work was not done, and a position that advances past undone work is a claim that it was. The changes stay pending and Needs You records the refusal.

## Related

- [Syncing an inbox](https://connectbyjbrh.com/docs/email/inbox-sync/)
- [IMAP UID and cursor lifecycle](https://connectbyjbrh.com/docs/email/imap-cursor/)
- [Connecting Gmail](https://connectbyjbrh.com/docs/email/connect-gmail/)
- [The daily email allowance](https://connectbyjbrh.com/docs/email/daily-allowance/)
- [Gmail API](https://connectbyjbrh.com/docs/technology/gmail-api/)
- [How a mail cursor loses messages, and how to stop it](https://connectbyjbrh.com/research/cursor-loss/)

## What this page is based on

- Connect source pack — channels, §1: Gmail via the Gmail API; the refusal that does not advance the read cursor
- Connect capability registry (`docs-source/facts.py`) — `gmail_history_sync`
- Connect source pack — the two mail models and the bridge (`docs-source/sources/GENERAL.md` §3)
