🤖 This issue was co-created in collaboration between a Human and an AI Assistant (Claude Opus 4.5). The human had the brilliant idea; the AI did the tedious research, format reverse-engineering, and documentation. Together we make a decent team! 🧑💻
Update: Issue revised with verified sources and real-world XML examples.
Summary
Add ability to export Gmail filters in the Atom XML format that Gmail WebUI can import, enabling users to:
- Share filter configurations between Gmail accounts
- Distribute standardized filter sets within teams
- Backup filters in a format that can be restored via Gmail Settings UI
Current State
gogcli currently supports:
gog gmail filters list # JSON or table output (API format)
gog gmail filters get <id> # Single filter details
gog gmail filters create # Create new filter
gog gmail filters delete <id> # Delete filter
Problem: The --json output uses Gmail API's JSON format, which is incompatible with Gmail's WebUI import feature (Settings → Filters → Import filters).
Proposed Solution
# Export all filters to WebUI-compatible XML
gog gmail filters export > mailFilters.xml
# Export specific filters by ID (optional enhancement)
gog gmail filters export --id=ABC123 > selected.xml
Technical Details
XML Format (Verified from Real Exports)
Gmail uses Atom Syndication Format (RFC 4287) with Google Apps namespace. Format verified from actual Gmail exports:
Real example from clouserw/gmailfilters (2009):
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>
<title>Mail Filters</title>
<id>tag:mail.google.com,2008:filters:1206327421108,1228202452022</id>
<updated>2009-12-09T20:44:01Z</updated>
<author>
<name>Wil Clouser</name>
<email>clouserw@gmail.com</email>
</author>
<entry>
<category term='filter'></category>
<title>Mail Filter</title>
<id>tag:mail.google.com,2008:filter:1206327421108</id>
<updated>2009-12-09T20:44:01Z</updated>
<content></content>
<apps:property name='from' value='bugzilla-daemon@mozilla.org'/>
<apps:property name='to' value='clouserw@gmail.com'/>
<apps:property name='hasTheWord' value='blocker'/>
<apps:property name='label' value='blocker'/>
</entry>
</feed>
Real example from dimagi/gmail-filters (2014):
<entry>
<category term='filter'></category>
<title>Mail Filter</title>
<id>tag:mail.google.com,2008:filter:1286460749536</id>
<updated>2014-09-19T17:40:28Z</updated>
<content></content>
<apps:property name='from' value='noreply@github.com'/>
<apps:property name='label' value='github'/>
<apps:property name='shouldArchive' value='true'/>
</entry>
Real example from dims' Kubernetes filters (2022):
<entry>
<category term='filter'></category>
<title>Mail Filter</title>
<id>tag:mail.google.com,2008:filter:1463531420708</id>
<updated>2022-06-25T21:39:52Z</updated>
<content></content>
<apps:property name='hasTheWord' value='list:"kubernetes-dev@googlegroups.com"'/>
<apps:property name='label' value='kubernetes'/>
<apps:property name='shouldArchive' value='true'/>
<apps:property name='sizeOperator' value='s_sl'/>
<apps:property name='sizeUnit' value='s_smb'/>
</entry>
Property Names (Verified from gmailctl source)
Criteria:
from, to, subject - Address/text matching
hasTheWord - Gmail search query syntax
doesNotHaveTheWord - Negative query
Actions:
label - Apply label (by name)
shouldArchive - Skip inbox (true)
shouldMarkAsRead - Mark read (true)
shouldTrash - Delete (true)
shouldNeverSpam - Never spam (true)
shouldStar - Star (true)
shouldAlwaysMarkAsImportant / shouldNeverMarkAsImportant - Importance
forwardTo - Forward address
smartLabelToApply - Category tab (^smartlabel_personal, ^smartlabel_social, etc.)
Size filtering (from dims' export):
sizeOperator - s_sl (larger) or s_ss (smaller)
sizeUnit - s_sb (bytes), s_skb (KB), s_smb (MB)
API → XML Conversion Required
| API JSON |
XML Property |
Notes |
criteria.from |
from |
Direct |
criteria.query |
hasTheWord |
Direct |
action.addLabelIds |
label |
Requires label ID→name lookup |
action.removeLabelIds["INBOX"] |
shouldArchive='true' |
Semantic conversion |
action.removeLabelIds["UNREAD"] |
shouldMarkAsRead='true' |
Semantic conversion |
Key implementation detail: API returns label IDs (e.g., Label_123), XML requires label names. Must call users.labels.list to build mapping.
Related Work
gmailctl already implements XML export:
This proves the format is well-understood and implementable.
Use Cases
- Team filter sharing - Export curated filters, share via wiki/docs
- Account migration - Backup filters in portable format
- Version control - Track filter changes in git
References
Official
Verified Real-World XML Exports
Implementation Reference
Format Analysis
Acceptance Criteria
🤖 This issue was co-created in collaboration between a Human and an AI Assistant (Claude Opus 4.5). The human had the brilliant idea; the AI did the tedious research, format reverse-engineering, and documentation. Together we make a decent team! 🧑💻
Update: Issue revised with verified sources and real-world XML examples.
Summary
Add ability to export Gmail filters in the Atom XML format that Gmail WebUI can import, enabling users to:
Current State
gogcli currently supports:
Problem: The
--jsonoutput uses Gmail API's JSON format, which is incompatible with Gmail's WebUI import feature (Settings → Filters → Import filters).Proposed Solution
Technical Details
XML Format (Verified from Real Exports)
Gmail uses Atom Syndication Format (RFC 4287) with Google Apps namespace. Format verified from actual Gmail exports:
Real example from clouserw/gmailfilters (2009):
Real example from dimagi/gmail-filters (2014):
Real example from dims' Kubernetes filters (2022):
Property Names (Verified from gmailctl source)
Criteria:
from,to,subject- Address/text matchinghasTheWord- Gmail search query syntaxdoesNotHaveTheWord- Negative queryActions:
label- Apply label (by name)shouldArchive- Skip inbox (true)shouldMarkAsRead- Mark read (true)shouldTrash- Delete (true)shouldNeverSpam- Never spam (true)shouldStar- Star (true)shouldAlwaysMarkAsImportant/shouldNeverMarkAsImportant- ImportanceforwardTo- Forward addresssmartLabelToApply- Category tab (^smartlabel_personal,^smartlabel_social, etc.)Size filtering (from dims' export):
sizeOperator-s_sl(larger) ors_ss(smaller)sizeUnit-s_sb(bytes),s_skb(KB),s_smb(MB)API → XML Conversion Required
criteria.fromfromcriteria.queryhasTheWordaction.addLabelIdslabelaction.removeLabelIds["INBOX"]shouldArchive='true'action.removeLabelIds["UNREAD"]shouldMarkAsRead='true'Key implementation detail: API returns label IDs (e.g.,
Label_123), XML requires label names. Must callusers.labels.listto build mapping.Related Work
gmailctl already implements XML export:
gmailctl export(Jsonnet config → Gmail XML)internal/engine/export/xml/marshal.goThis proves the format is well-understood and implementable.
Use Cases
References
Official
Verified Real-World XML Exports
Implementation Reference
Format Analysis
Acceptance Criteria
gog gmail filters exportoutputs valid Atom XMLusers.labels.listSTARRED→shouldStar, etc.)"→")