references/gmail-api.md — Approval-Gated Mail
From version 0.2.1 of Approval-Gated Mail, shown publicly by its creator. 7.2 KiB · SHA-256 1ebd1c21ab762801f70fa36280838cb5a2029f07a9a36680efa00ecea616e2b8.
# Gmail API reference
This documents the 0.2.1 Gmail and Google Workspace backend. It uses a
user-owned Desktop OAuth client and delegated consent; it ships no client ID.
## Authentication and scopes
Installed-app authorization uses PKCE S256, random state, and a random-port
loopback redirect on 127.0.0.1. The CLI prints a consent URL on
`accounts.google.com`; open it yourself. The CLI never fetches that host or
opens a browser. It loops until a valid callback or the 180 s timeout, checks
state, and exchanges
the code at `https://oauth2.googleapis.com/token`. Refresh uses that host;
revoke uses `https://oauth2.googleapis.com/revoke`. An `invalid_grant`
requires `auth login` again. External Testing grants expire after 7 days.
Sources: <https://developers.google.com/identity/protocols/oauth2/native-app>,
<https://developers.google.com/workspace/gmail/api/quickstart/python>,
<https://support.google.com/cloud/answer/15549945>.
| Scope | Classification | Purpose |
| --- | --- | --- |
| `https://www.googleapis.com/auth/gmail.readonly` | Restricted | Read mail, threads, metadata, and sent records |
| `https://www.googleapis.com/auth/gmail.compose` | Restricted | Draft and send after the CLI approval gate |
Compose allows sending as well as drafting. The CLI digest is the send
control. Google User Data Policy and Limited Use apply. A user-owned
personal-use client may be exempt from restricted-scope verification, but
Google's guidance does not fully resolve the obligations for this
bring-your-own-client tool. Sources:
<https://developers.google.com/identity/protocols/oauth2/production-readiness/restricted-scope-verification>,
<https://developers.google.com/terms/api-services-user-data-policy>.
Both requested scopes permit users.getProfile; no extra scope is requested.
Reference: <https://developers.google.com/workspace/gmail/api/reference/rest/v1/users/getProfile>.
## API operations
API requests target `https://gmail.googleapis.com/gmail/v1/users/me`.
The host guard is re-checked on redirects. Listing follows `nextPageToken`
only to the requested count. Search quotes the user's Gmail `q` text and
refuses `"` and `\`; sender search uses a quoted `from:` term with the same refusal. MIME reads prefer
text/plain, otherwise convert HTML to text. Attachment listing requests `format=full`
with `fields` selecting part IDs, filenames, MIME types, sizes and attachment
IDs only through 50 nested part levels, excluding body data. Full read/thread responses can contain inline attachment
data; parts with filenames, attachment IDs or Content-Disposition attachment are
discarded, never shown or saved. No attachment-download endpoint is called.
Sources: <https://developers.google.com/workspace/gmail/api/reference/rest/v1/users.messages/get>,
<https://developers.google.com/workspace/gmail/api/guides/performance#partial-response>.
References were not refreshed during this offline synthetic review.
| Command | Gmail path |
| --- | --- |
| send/draft preview and pre-write identity check | `GET /profile` |
| recent, search, from, verify-sent | `GET /messages` |
| read, attachments, reply preview | `GET /messages/{id}` |
| thread | `GET /threads/{id}` |
| send | `POST /messages/send` |
| draft | `POST /drafts` |
The raw send payload is base64url RFC 5322 content. A reply preview reads
Message-ID, References, Subject, and threadId. The digest binds the resulting
In-Reply-To, References, threadId, method, URL, and exact raw payload.
Live mode re-fetches and refuses changed context. Every Gmail preview reads
`GET https://gmail.googleapis.com/gmail/v1/users/me/profile`, shows the stripped,
casefolded `emailAddress`, and binds it into the digest. Immediately before each
Gmail API write, the same write token reads the profile again; missing or changed
identity refuses the write. Dry-run prints a random RFC Message-ID; live send and draft
must pass it back with `--message-id`, along with `--approve`. This flag is
Gmail-only and accepts strict ASCII dot-atom IDs: the form is `<local@domain>`
with no spaces; local parts use ASCII dot-atom characters and domains use
ASCII letters, digits, dots and hyphens. Reply-target IDs fetched from Gmail remain lenient.
Sent verification requires
the returned SENT label and exact rfc822msgid match. Standalone
`verify-sent` requires `--subject` and `--rfc822msgid`; see correlation limits below. Reads can retry 429 and 5xx with backoff and Retry-After;
send and draft are never blindly retried after an unknown outcome. Before either
live POST, `GET /messages?q=rfc822msgid:<message-id>&includeSpamTrash=true`
refuses any existing match. Gmail reads use the stored token's authorized account;
`--from` and `GMAIL_DEFAULT_USER` only set the From header. Any authenticated
Gmail command may refresh the token and rewrite its local store.
New projects receive 6,000 quota units per minute per user per project.
`messages.send` costs 100 units and `drafts.create` 10. Google documents
up to 500 recipients per message; personal Gmail permits about 500 sends
per day and paid Workspace about 2,000. These limits may change.
Source: <https://developers.google.com/workspace/gmail/api/reference/quota>.
For OAuth production readiness and Internal versus External audience, see
<https://developers.google.com/identity/protocols/oauth2/production-readiness/overview>.
Product naming follows <https://about.google/brand-resource-center/guidance/>.
## Sent verification limits
Automatic `send` reads metadata for the server ID returned by the POST and
requires the SENT label and exact RFC Message-ID from the approved dry run.
It does not compare Subject or recipients. Standalone verification reads up to
`--count` SENT entries (default 25) and their metadata; it requires SENT, an
exact RFC Message-ID, an exact case-sensitive decoded Subject, and equality
of case-insensitive parsed address sets for each supplied recipient flag.
Order and repeated addresses do not matter; omitted flags skip that field.
Use empty flags to require no Cc or Bcc, and copy every approved address:
```bash
python3 scripts/m365_mail.py --provider gmail verify-sent --subject "Approved subject" --rfc822msgid '<ID@example.com>' --to recipient@example.com --cc "" --bcc "" --count 25
```
Standalone verification makes one bounded scan, with no visibility polling or
baseline exclusion. SENT visibility can lag a send; a missing match or one
outside the count bound leaves the outcome unknown. Reconcile before retrying.
Duplicate subjects alone cannot match because the RFC Message-ID must also
match; reused Message-IDs with the same subject and recipient sets remain
ambiguous. Neither check compares body, sender, attachments, approval digest,
or a send-time window; timestamps are reported, not matched. These checks
confirm a provider sent record under their stated conditions, not exact
approved content, downstream delivery, recipient acceptance, or reading.
## Network and storage
The CLI contacts `gmail.googleapis.com` and `oauth2.googleapis.com`,
plus its local 127.0.0.1 listener. `accounts.google.com` is a browser
destination only. The opener honors `HTTPS_PROXY`, `https_proxy`,
`NO_PROXY`, `no_proxy`, and macOS or Windows system proxy settings; TLS
terminates at declared hosts. No telemetry or publisher server is used.
Tokens stay in macOS Keychain or a private local 0600 file.
By Zero Width Lab (zerowidthlab.com)
License: MIT (read it).
MIT
MIT LicenseCopyright (c) 2026 Zero Width Lab
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.