> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ancha.io/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> These docs describe the Ancha CLI, npm package @ancha-labs/cli version 0.1.4, and the hosted workspace at app.ancha.io. Coding agents should start at https://docs.ancha.io/coding-agents. Run the CLI as `ancha` after `npm install -g @ancha-labs/cli`, or as `npx -y @ancha-labs/cli@0.1.4` without installing. Pass --json for machine-readable output, or set ANCHA_JSON=1. Every command ends with a `next:` line naming the next command. Exit codes: 0 pass, 1 a check failed or a comparison regressed, 2 usage, config or authentication error, 3 infrastructure error, 4 unimplemented endpoint with --strict.

# Gmail

> A stateful replica of the Gmail API v1: mailboxes, messages, threads, labels, drafts, attachments and history, where mail moves only between the mailboxes in the world.

The Gmail replica implements 26 endpoints of the Gmail API v1 for the mailboxes in the world. Sending a message delivers it to a seeded mailbox or records it in Sent; nothing leaves the world. Its key is `gmail`. It was tested with `@googleapis/gmail`.

## Connect

| Variable | Value |
| - | - |
| `GMAIL_API_URL` | Base URL. Paths start with `/gmail/v1/`. |
| `GMAIL_ACCESS_TOKEN` | Fake OAuth access token for the agent's mailbox. Send `Authorization: Bearer <token>`. |

```js Node theme={null}
import { auth, gmail } from '@googleapis/gmail';
const oauth = new auth.OAuth2();
oauth.setCredentials({ access_token: process.env.GMAIL_ACCESS_TOKEN });
const mail = gmail({ version: 'v1', auth: oauth });
const options = { rootUrl: process.env.GMAIL_API_URL + '/', retry: false };
const { data } = await mail.users.messages.list({ userId: 'me', q: 'is:unread' }, options);
```

This is what `ancha connect gmail --lang ts` prints. Google's client takes the replica as a per-request `rootUrl` ending in `/`; a global `rootUrl` keeps only the origin and drops the path. Use `me` as the user ID.

## Endpoints

| Method | Path | Support |
| - | - | - |
| `GET` | `/gmail/v1/users/:userId/profile` | supported |
| `GET` | `/gmail/v1/users/:userId/messages` | partial |
| `GET` | `/gmail/v1/users/:userId/messages/:id` | supported |
| `POST` | `/gmail/v1/users/:userId/messages/send` | partial |
| `POST` | `/gmail/v1/users/:userId/messages/batchModify` | supported |
| `POST` | `/gmail/v1/users/:userId/messages/:id/modify` | supported |
| `POST` | `/gmail/v1/users/:userId/messages/:id/trash` | supported |
| `POST` | `/gmail/v1/users/:userId/messages/:id/untrash` | supported |
| `GET` | `/gmail/v1/users/:userId/messages/:messageId/attachments/:id` | supported |
| `GET` | `/gmail/v1/users/:userId/threads` | partial |
| `GET` | `/gmail/v1/users/:userId/threads/:id` | supported |
| `POST` | `/gmail/v1/users/:userId/threads/:id/modify` | supported |
| `POST` | `/gmail/v1/users/:userId/threads/:id/trash` | supported |
| `POST` | `/gmail/v1/users/:userId/threads/:id/untrash` | supported |
| `GET` | `/gmail/v1/users/:userId/labels` | supported |
| `GET` | `/gmail/v1/users/:userId/labels/:id` | supported |
| `POST` | `/gmail/v1/users/:userId/labels` | partial |
| `PATCH` | `/gmail/v1/users/:userId/labels/:id` | supported |
| `DELETE` | `/gmail/v1/users/:userId/labels/:id` | supported |
| `GET` | `/gmail/v1/users/:userId/drafts` | supported |
| `GET` | `/gmail/v1/users/:userId/drafts/:id` | supported |
| `POST` | `/gmail/v1/users/:userId/drafts` | partial |
| `PUT` | `/gmail/v1/users/:userId/drafts/:id` | partial |
| `POST` | `/gmail/v1/users/:userId/drafts/send` | supported |
| `DELETE` | `/gmail/v1/users/:userId/drafts/:id` | supported |
| `GET` | `/gmail/v1/users/:userId/history` | partial |

Anything else returns HTTP 501 with Google's error shape and `x-ancha-gap: 1`.

## Behavior

* **Mail stays in the world.** A message sent to a seeded mailbox is delivered there. Mail to any other address is recorded in Sent and goes nowhere. An unauthorized `From` becomes the account's own address.
* **Search.** `q` supports words, phrases, `from:`, `to:`, `cc:`, `bcc:`, `subject:`, `label:`, `in:`, `is:`, `has:`, `category:`, `filename:`, dates, sizes, `rfc822msgid:`, `OR`, `-` and grouping. Other operators are coverage gaps. A thread matches when any of its messages matches.
* **Sending** takes `raw` only; the `/upload` media form is a gap. Sending a draft gives the message a new ID and removes the draft. Replacing a draft creates a new draft message, as Gmail does.
* **Labels** take colors as `#rrggbb`; the replica does not check them against Gmail's palette.
* **History** starts after seeding, with one record per message change.

## Starter data

The `gmail-starter` dataset has two mailboxes at `acme.example`. The agent's token belongs to **Acme Support**, `support@acme.example`, with the labels Customers, Billing, Follow up and Sales, nineteen messages and one draft.

| Ref | Message |
| - | - |
| `$sso_question`, `$sso_answer`, `$soc2` | Grace Lin at Bluefield Logistics asks about SSO with Okta and SCIM; Laura Kim answers; Grace asks for the SOC 2 Type II report (labels Customers and Follow up). |
| `$double_count`, `$double_count_answer`, `$double_count_thanks` | Carla Diaz at Kestrel Credit Union reports a dashboard counting yesterday twice; Mike Turner points at the 4.12 fix; Carla confirms. |
| `$pricing`, `$pricing_draft` | Lucy Grant at Harbor Foods asks about Team plan pricing (label Sales). An unsent draft reply is in Drafts. |
| `$seats`, `$seats_answer`, `$seats_followup` | Nora Bell at Pinecrest Health adds 20 seats; Laura confirms proration; Nora asks for the order form (unread, Billing). |
| `$csv_export` | Erin Walsh at Ridgeway Robotics cannot find last month's CSV export, 36 minutes before start. Unread, important. |
| `$summit_training` | Megan Shaw at Summit Freight asks for the admin training recording. Unread. |
| `$renewal_po` | Peter Novak at Kestrel Credit Union sends the renewal purchase order, with a CSV attachment. Unread. |
| `$ridgeway_onboarding` | Mike Turner: Ridgeway Robotics onboards its warehouse team this week. |
| `$datadog_recovered`, `$github_rate_limit` | Notifications: a recovered Datadog monitor and a GitHub pull request. |
| `$ridgeway_autoreply`, `$conference`, `$domain_spam` | An out-of-office reply in the trash, a promotion, and a message in Spam. |

The second mailbox, `laura@acme.example` (Laura Kim), holds one sent message about the Helix Manufacturing quarterly review.

## Checks

Gmail has no state checks. Use trace checks, for example `{ called: "POST /gmail/v1/users/me/messages/send" }` or `{ not_called: "POST /gmail/v1/users/me/messages/*/trash" }`.

## Limits

* Mail moves only inside the world: delivery to seeded mailboxes, Sent for everything else.
* Uploads, batch requests, push notifications, filters, settings and delegation are not modeled.


## Related topics

- [Tools](https://docs.ancha.io/tools/overview.md)
- [Test file (scene.yaml)](https://docs.ancha.io/reference/test-file.md)
- [ancha connect](https://docs.ancha.io/cli/connect.md)
- [Introduction](https://docs.ancha.io/index.md)
- [Environment variables](https://docs.ancha.io/reference/environment-variables.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.