home / skills / vm0-ai / vm0-skills / gmail

gmail skill

/gmail

This skill enables reading, composing, labeling, and managing Gmail messages via curl calls with OAuth tokens.

npx playbooks add skill vm0-ai/vm0-skills --skill gmail

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
11.9 KB
---
name: gmail
description: Gmail API via curl. Use this skill to read, send, and manage emails, labels, drafts, and threads.
vm0_secrets:
  - GMAIL_CLIENT_SECRET
  - GMAIL_REFRESH_TOKEN
vm0_vars:
  - GMAIL_CLIENT_ID
---

# Gmail API

Read, send, and manage emails via Google's Gmail REST API.

> Official docs: https://developers.google.com/workspace/gmail/api/reference/rest

---

## When to Use

Use this skill when you need to:

- Read and search emails
- Send emails or reply to threads
- Manage drafts
- Create and manage labels
- List and modify threads
- Get user profile information

---

## Prerequisites

### 1. Create Google Cloud Project

1. Go to https://console.cloud.google.com
2. Create a new project or select existing
3. Enable Gmail API: https://console.cloud.google.com/apis/library/gmail.googleapis.com

### 2. Configure OAuth Consent Screen

1. Go to https://console.cloud.google.com/apis/credentials/consent
2. Select **External** → Create
3. Fill required fields (app name, support email, developer email)
4. Click **Save and Continue** through Scopes (skip adding scopes)
5. In **Audience** section, click **Add Users** and add your Gmail address as test user
6. Save and continue to finish

### 3. Create OAuth Client ID

1. Go to https://console.cloud.google.com/apis/credentials
2. Click **Create Credentials** → **OAuth client ID**
3. Choose **Web application** (not Desktop)
4. Add Authorized redirect URI: `https://developers.google.com/oauthplayground`
5. Click Create and note the **Client ID** and **Client Secret**

### 4. Get Refresh Token (OAuth Playground)

1. Go to https://developers.google.com/oauthplayground/
2. Click **Settings** (gear icon ⚙️) → Check **Use your own OAuth credentials**
3. Enter your Client ID and Client Secret
4. In the left panel, enter scope: `https://www.googleapis.com/auth/gmail.modify`
5. Click **Authorize APIs** → Sign in with your test user account
6. Click **Exchange authorization code for tokens**
7. Copy the **Refresh token**

### 5. Set Environment Variables

```bash
export GMAIL_CLIENT_ID="your-client-id"
export GMAIL_CLIENT_SECRET="your-client-secret"
export GMAIL_REFRESH_TOKEN="your-refresh-token"
```

---

## Get Access Token

Access tokens expire after 1 hour. Use refresh token to get a new one and save to `/tmp`:

```bash
bash -c 'curl -s -X POST "https://oauth2.googleapis.com/token" -d "client_id=$GMAIL_CLIENT_ID" -d "client_secret=$GMAIL_CLIENT_SECRET" -d "refresh_token=$GMAIL_REFRESH_TOKEN" -d "grant_type=refresh_token"' | jq -r '.access_token' > /tmp/gmail_token.txt

# Verify token was obtained
head -c 20 /tmp/gmail_token.txt && echo "..."
```

> **Important:** When using `$VAR` in a command that pipes to another command, wrap the command containing `$VAR` in `bash -c '...'`. Due to a Claude Code bug, environment variables are silently cleared when pipes are used directly.

> **Placeholders:** Values in `{curly-braces}` like `{message-id}` are placeholders. Replace them with actual values when executing.

---

## User Profile

### Get Profile

```bash
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/profile" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"'
```

---

## Messages

### List Messages

```bash
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/messages?maxResults=10" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"'
```

### List Messages with Query

Search using Gmail query syntax:

```bash
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/messages?q=is:unread&maxResults=10" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"'
```

Common queries:
- `is:unread` - Unread messages
- `from:[email protected]` - From specific sender
- `subject:hello` - Subject contains "hello"
- `after:2024/01/01` - After date
- `has:attachment` - Has attachments
- `label:INBOX` - In inbox

### Get Message

```bash
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/messages/{message-id}" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"'
```

### Get Message (Metadata Only)

```bash
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/messages/{message-id}?format=metadata&metadataHeaders=From&metadataHeaders=Subject&metadataHeaders=Date" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"'
```

### Send Email

```bash
# Create RFC 2822 message and base64url encode
RAW_MESSAGE=$(echo -e "To: {recipient-email}\r\nSubject: {subject}\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n{body-text}" | base64 | tr '+/' '-_' | tr -d '=')
```

Write to `/tmp/gmail_request.json`:

```json
{
  "raw": "$RAW_MESSAGE"
}
```

Then run:

```bash
bash -c 'curl -s -X POST "https://gmail.googleapis.com/gmail/v1/users/me/messages/send" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)" --header "Content-Type: application/json" -d @/tmp/gmail_request.json'
```

### Reply to Thread

```bash
# Include In-Reply-To and References headers for proper threading
RAW_MESSAGE=$(echo -e "To: {recipient-email}\r\nSubject: Re: {original-subject}\r\nIn-Reply-To: <{original-message-id}>\r\nReferences: <{original-message-id}>\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n{reply-text}" | base64 | tr '+/' '-_' | tr -d '=')
```

Write to `/tmp/gmail_request.json`:

```json
{
  "raw": "$RAW_MESSAGE",
  "threadId": "{thread-id}"
}
```

Then run:

```bash
bash -c 'curl -s -X POST "https://gmail.googleapis.com/gmail/v1/users/me/messages/send" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)" --header "Content-Type: application/json" -d @/tmp/gmail_request.json'
```

### Modify Message Labels

Write to `/tmp/gmail_request.json`:

```json
{
  "addLabelIds": ["STARRED"],
  "removeLabelIds": ["UNREAD"]
}
```

Then run:

```bash
bash -c 'curl -s -X POST "https://gmail.googleapis.com/gmail/v1/users/me/messages/{message-id}/modify" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)" --header "Content-Type: application/json" -d @/tmp/gmail_request.json'
```

### Trash Message

```bash
bash -c 'curl -s -X POST "https://gmail.googleapis.com/gmail/v1/users/me/messages/{message-id}/trash" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"'
```

### Delete Message Permanently

```bash
bash -c 'curl -s -X DELETE "https://gmail.googleapis.com/gmail/v1/users/me/messages/{message-id}" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"'
```

---

## Threads

### List Threads

```bash
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/threads?maxResults=10" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"'
```

### Get Thread

```bash
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/threads/{thread-id}" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"'
```

### Trash Thread

```bash
bash -c 'curl -s -X POST "https://gmail.googleapis.com/gmail/v1/users/me/threads/{thread-id}/trash" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"'
```

---

## Labels

### List Labels

```bash
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/labels" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"' | jq '.labels[] | {id, name, type}'
```

### Create Label

Write to `/tmp/gmail_request.json`:

```json
{
  "name": "{label-name}",
  "labelListVisibility": "labelShow",
  "messageListVisibility": "show"
}
```

Then run:

```bash
bash -c 'curl -s -X POST "https://gmail.googleapis.com/gmail/v1/users/me/labels" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)" --header "Content-Type: application/json" -d @/tmp/gmail_request.json'
```

### Delete Label

```bash
bash -c 'curl -s -X DELETE "https://gmail.googleapis.com/gmail/v1/users/me/labels/{label-id}" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"'
```

---

## Drafts

### List Drafts

```bash
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/drafts" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"'
```

### Create Draft

```bash
RAW_MESSAGE=$(echo -e "To: {recipient-email}\r\nSubject: {subject}\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n{body-text}" | base64 | tr '+/' '-_' | tr -d '=')
```

Write to `/tmp/gmail_request.json`:

```json
{
  "message": {
    "raw": "$RAW_MESSAGE"
  }
}
```

Then run:

```bash
bash -c 'curl -s -X POST "https://gmail.googleapis.com/gmail/v1/users/me/drafts" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)" --header "Content-Type: application/json" -d @/tmp/gmail_request.json'
```

### Send Draft

Write to `/tmp/gmail_request.json`:

```json
{
  "id": "{draft-id}"
}
```

Then run:

```bash
bash -c 'curl -s -X POST "https://gmail.googleapis.com/gmail/v1/users/me/drafts/send" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)" --header "Content-Type: application/json" -d @/tmp/gmail_request.json'
```

### Delete Draft

```bash
bash -c 'curl -s -X DELETE "https://gmail.googleapis.com/gmail/v1/users/me/drafts/{draft-id}" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"'
```

---

## Attachments

### Get Attachment

```bash
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/messages/{message-id}/attachments/{attachment-id}" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"' | jq -r '.data' | base64 -d > attachment.bin
```

---

## Settings

### Get Vacation Settings

```bash
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/settings/vacation" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"'
```

### Update Vacation Settings

Write to `/tmp/gmail_request.json`:

```json
{
  "enableAutoReply": true,
  "responseSubject": "Out of Office",
  "responseBodyPlainText": "I am currently out of office.",
  "restrictToContacts": false,
  "restrictToDomain": false
}
```

Then run:

```bash
bash -c 'curl -s -X PUT "https://gmail.googleapis.com/gmail/v1/users/me/settings/vacation" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)" --header "Content-Type: application/json" -d @/tmp/gmail_request.json'
```

### List Filters

```bash
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/settings/filters" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"'
```

### Create Filter

Write to `/tmp/gmail_request.json`:

```json
{
  "criteria": {
    "from": "{filter-email}"
  },
  "action": {
    "addLabelIds": ["TRASH"],
    "removeLabelIds": ["INBOX"]
  }
}
```

Then run:

```bash
bash -c 'curl -s -X POST "https://gmail.googleapis.com/gmail/v1/users/me/settings/filters" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)" --header "Content-Type: application/json" -d @/tmp/gmail_request.json'
```

---

## Common Scopes

| Scope | Permission |
|-------|------------|
| `gmail.readonly` | Read-only access |
| `gmail.send` | Send emails only |
| `gmail.compose` | Create drafts and send |
| `gmail.modify` | Read, send, delete, manage |
| `gmail.labels` | Manage labels only |
| `gmail.settings.basic` | Manage basic settings |
| `gmail.settings.sharing` | Manage sensitive settings |

Use full URL: `https://www.googleapis.com/auth/gmail.modify`

---

## Decode Message Body

Gmail returns message body as base64url encoded. To decode:

```bash
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/messages/{message-id}" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"' | jq -r '.payload.body.data // .payload.parts[0].body.data' | tr '_-' '/+' | base64 -d
```

---

## Guidelines

1. **Token Refresh**: Access tokens expire in 1 hour; always refresh before API calls
2. **Rate Limits**: Gmail API has quota limits; implement exponential backoff
3. **Batch Requests**: Use batch endpoints for multiple operations
4. **Message Format**: Messages must be RFC 2822 compliant and base64url encoded
5. **Scopes**: Request minimum required scopes for your use case

---

## API Reference

- REST Reference: https://developers.google.com/workspace/gmail/api/reference/rest
- Guides: https://developers.google.com/workspace/gmail/api/guides
- OAuth Playground: https://developers.google.com/oauthplayground/
- Scopes: https://developers.google.com/identity/protocols/oauth2/scopes#gmail

Overview

This skill provides ready-to-run curl examples for the Gmail REST API to read, send, and manage emails, labels, drafts, threads, attachments, and basic settings. It focuses on practical shell commands, token refresh steps, and JSON payload patterns so you can automate Gmail tasks from scripts or CI pipelines. The examples assume OAuth credentials and a refresh token are available as environment variables.

How this skill works

Use the refresh token to obtain a short-lived access token, save it to a temporary file, and include it in the Authorization header for each Gmail REST call. The skill shows curl commands for listing and retrieving messages and threads, creating and sending RFC 2822 messages (base64url encoded), modifying labels, managing drafts, and working with attachments and settings. It also documents common query filters, scopes, and decoding of base64url message bodies.

When to use it

  • Automating inbox processing, search, labeling, or triage tasks from shell scripts.
  • Sending emails or threaded replies programmatically from a server or CI job.
  • Creating, listing, or sending drafts as part of a workflow.
  • Managing labels and filters to automate message routing.
  • Downloading attachments or decoding message bodies for processing.

Best practices

  • Store client credentials and refresh tokens in secure environment variables, not in code.
  • Always refresh the access token before API calls; access tokens expire after one hour.
  • Use minimal OAuth scopes required for the task to reduce risk.
  • Encode messages as RFC 2822 and base64url-encode raw payloads for send/create endpoints.
  • Respect Gmail quota limits and implement exponential backoff on 429/5xx responses.

Example use cases

  • Run a nightly script that searches unread messages, saves attachments, and marks them read.
  • Send automated notifications or reports from a server using the messages/send endpoint.
  • Create drafts from a web form and allow a human to review before sending.
  • Auto-label incoming emails from specific senders and move spam-like messages to TRASH.
  • Export thread history for archiving by listing threads and fetching their messages.

FAQ

Do I need a web application OAuth client and redirect URI?

Yes — create a Web application OAuth client and use an OAuth redirect (the OAuth Playground is a convenient redirect for obtaining a refresh token).

How do I thread replies correctly?

Include In-Reply-To and References headers with the original message ID and set threadId in the send payload so Gmail groups messages into the same thread.