Gmail
The Gmail integration gives the agent your mailbox: it can read, search, send, and organize email, and it is notified when new mail arrives. You connect once through Google's consent screen, and everything after that runs through the Gmail API.
Requirements
| Requirement |
Details |
| Google account |
Any Gmail or Google Workspace account |
| A browser |
For the one-time OAuth consent |
| CraftBot running |
Connect from Settings → Integrations in the browser interface |
| OAuth app |
None needed. Release builds embed a CraftOS Google client (GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET override it for self-hosted setups) |
Setup
- Open Settings → Integrations and click Connect on the Gmail card. You can also run
/gmail login in chat.
- Your browser opens Google's consent screen. CraftBot requests three scopes:
gmail.modify (read, send, and organize mail), plus userinfo.email and userinfo.profile (to record which account is connected).
- Approve access. Google redirects back to
http://localhost:8765, where CraftBot exchanges the code for tokens.
- CraftBot confirms with "Gmail connected as your address". The credential is saved locally as
gmail.json.
- Verify any time with
/gmail status or /cred status.
Connecting Gmail grants mail scopes only. Google Calendar, Drive, Docs, and YouTube each have their own card, their own consent, and their own credential file.
How it connects
Authentication. OAuth 2.0 authorization code flow with PKCE, using the shared CraftOS Google client. Consent is requested with offline access, so CraftBot receives a refresh token along with the access token.
Token refresh. Access tokens expire after about an hour. Before each API call CraftBot checks the stored expiry and refreshes the token automatically with the refresh token, writing the new token back to gmail.json. You only reconnect if Google revokes the refresh token, for example after a password change.
Listener. Gmail is the only Google integration that listens for events. A polling loop checks Gmail's history API every 5 seconds for messages newly added to your inbox. Each new message from someone else is delivered to the agent as a platform message carrying the sender, subject, and snippet. In practice this means new mail can wake the agent: an incoming email enters the same trigger queue as a chat message, and the agent can read it, act on it, or deliberately ignore it. Mail you sent yourself is skipped. If a poll fails, the listener retries after 10 seconds and re-syncs its position in the history feed.
What the agent can do
All 34 Gmail actions, grouped by domain. Each purpose comes from the action's registered description.
Messages
| Action |
Purpose |
send_gmail |
Send an email via Gmail |
list_gmail |
List recent emails from the Gmail inbox |
get_gmail |
Get details of a specific message by ID; with full_body=true includes body text and an attachments list |
read_top_emails |
Read the top N recent emails with details |
search_gmail |
Search Gmail using Gmail's q syntax (e.g. from:alice subject:invoice newer_than:7d has:attachment) |
reply_gmail |
Reply to a message, preserving thread and In-Reply-To/References headers; reply_all=true also CCs the original To/Cc |
forward_gmail |
Forward a message to another address |
modify_gmail_labels |
Add/remove labels on a message (common IDs: INBOX, UNREAD, STARRED, IMPORTANT, TRASH, SPAM) |
trash_gmail |
Move a message to Trash (soft delete; recoverable for 30 days) |
untrash_gmail |
Recover a message from Trash |
delete_gmail |
Permanently delete a message. Irreversible; prefer trash_gmail |
batch_modify_gmail |
Bulk add/remove labels across multiple messages in one call |
batch_delete_gmail |
Permanently delete multiple messages. Irreversible |
Threads
| Action |
Purpose |
list_gmail_threads |
List conversation threads |
get_gmail_thread |
Get a thread and its messages |
modify_gmail_thread_labels |
Add/remove labels on every message in a thread |
trash_gmail_thread |
Move an entire thread to Trash |
untrash_gmail_thread |
Recover a thread from Trash |
delete_gmail_thread |
Permanently delete a thread (all messages). Irreversible |
Drafts
| Action |
Purpose |
list_gmail_drafts |
List drafts |
get_gmail_draft |
Get a draft by ID |
create_gmail_draft |
Create a draft (not sent); returns the draft ID for later edit/send |
update_gmail_draft |
Replace a draft's content (all fields required) |
send_gmail_draft |
Send a previously created draft |
delete_gmail_draft |
Permanently delete a draft |
Labels
| Action |
Purpose |
list_gmail_labels |
List all labels (system and user) |
get_gmail_label |
Get a single label by ID |
create_gmail_label |
Create a new user label, with optional colors and visibility |
update_gmail_label |
Update (rename / recolor) a label |
delete_gmail_label |
Delete a label (also removes it from all messages and threads) |
Attachments and profile
| Action |
Purpose |
download_gmail_attachment |
Download an attachment to a local path (get IDs from get_gmail with full_body=true) |
get_gmail_profile |
Get the connected account's profile: email address, message/thread totals, history ID |
Google Workspace wrappers
Two generic email actions are registered alongside the Gmail set and route through the same client.
| Action |
Purpose |
send_google_workspace_email |
Send email via Google Workspace |
read_recent_google_workspace_emails |
Read recent emails |
Example requests
- "Summarize my unread email from this week."
- "Reply to the latest message from Alice and confirm that Thursday works."
- "Find every email with an invoice attachment from the last 30 days and download the attachments to my workspace."
- "Create a Newsletters label, apply it to everything from newsletters@example.com, and archive those messages."
- "Every weekday at 8am, send me a digest of my unread inbox on Telegram." This creates a recurring schedule that fires a task each morning. See Scheduling.
- "When an email from my landlord arrives, draft a reply and show it to me before sending." The 5-second listener makes this possible: the incoming email itself triggers the agent.
Configuration
- Auto-process incoming emails. A checkbox on the Gmail integration card, stored as
process_incoming in gmail_config.json (default on). When on, every new inbox message is forwarded to the agent. Turn it off to make Gmail effectively send-only: the listener keeps the connection alive, but incoming mail no longer reaches the agent.
- Own OAuth app. Set
GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET as environment variables to use your own Google Cloud OAuth client instead of the embedded one. See Credentials.
- Credential file. The token lives in
gmail.json in the local credential store. /gmail logout removes it. The Google Workspace meta-integration writes the same file when you connect everything at once, so the two stay interchangeable.
Troubleshooting
| Symptom |
Likely cause |
Fix |
| Consent completes but CraftBot never confirms |
Port 8765 is blocked or in use, so the redirect never arrived |
Free the port and run /gmail login again |
| Actions fail with "No gmail credentials" |
Not connected, or logged out |
Connect from Settings → Integrations or run /gmail login |
| Agent does not react to new mail |
Auto-process incoming emails is off, or the listener hit an error |
Check the toggle on the integration card, then check logs/ for [GMAIL] entries |
delete_gmail returns a permissions error |
Permanent deletion needs a broader scope than gmail.modify |
Use trash_gmail instead (recoverable for 30 days) |
| Every request fails with 401 after working fine |
Google revoked the refresh token (password change, security review) |
/gmail logout, then reconnect |
Next