Gmail API via curl. Use this skill to read, send, and manage emails, labels, drafts, and threads.
/plugin marketplace add vm0-ai/api0/plugin install api0@api0This skill inherits all available tools. When active, it can use any tool Claude has access to.
Read, send, and manage emails via Google's Gmail REST API.
Official docs: https://developers.google.com/workspace/gmail/api/reference/rest
Use this skill when you need to:
https://developers.google.com/oauthplaygroundhttps://www.googleapis.com/auth/gmail.modifyexport GMAIL_CLIENT_ID="your-client-id"
export GMAIL_CLIENT_SECRET="your-client-secret"
export GMAIL_REFRESH_TOKEN="your-refresh-token"
Access tokens expire after 1 hour. Use refresh token to get a new one and save to /tmp:
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
$VARin a command that pipes to another command, wrap the command containing$VARinbash -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.
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/profile" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"' | jq .
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/messages?maxResults=10" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"' | jq .
Search using Gmail query syntax:
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)"' | jq .
Common queries:
is:unread - Unread messagesfrom:example@gmail.com - From specific sendersubject:hello - Subject contains "hello"after:2024/01/01 - After datehas:attachment - Has attachmentslabel:INBOX - In inboxbash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/messages/{message-id}" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"' | jq .
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)"' | jq .
# 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 '=')
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 "{\"raw\": \"'"$RAW_MESSAGE"'\"}"' | jq .
# 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 '=')
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 "{\"raw\": \"'"$RAW_MESSAGE"'\", \"threadId\": \"{thread-id}\"}"' | jq .
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 '"'"'{"addLabelIds": ["STARRED"], "removeLabelIds": ["UNREAD"]}'"'"'' | jq .
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)"' | jq .
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)"'
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/threads?maxResults=10" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"' | jq .
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/threads/{thread-id}" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"' | jq .
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)"' | jq .
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}'
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 '"'"'{"name": "{label-name}", "labelListVisibility": "labelShow", "messageListVisibility": "show"}'"'"'' | jq .
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)"'
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/drafts" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"' | jq .
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 '=')
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 "{\"message\": {\"raw\": \"'"$RAW_MESSAGE"'\"}}"' | jq .
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 '"'"'{"id": "{draft-id}"}'"'"'' | jq .
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)"'
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
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/settings/vacation" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"' | jq .
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 '"'"'{"enableAutoReply": true, "responseSubject": "Out of Office", "responseBodyPlainText": "I am currently out of office.", "restrictToContacts": false, "restrictToDomain": false}'"'"'' | jq .
bash -c 'curl -s "https://gmail.googleapis.com/gmail/v1/users/me/settings/filters" --header "Authorization: Bearer $(cat /tmp/gmail_token.txt)"' | jq .
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 '"'"'{"criteria": {"from": "{filter-email}"}, "action": {"addLabelIds": ["TRASH"], "removeLabelIds": ["INBOX"]}}'"'"'' | jq .
| 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
Gmail returns message body as base64url encoded. To decode:
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