106 lines
3.6 KiB
Markdown
106 lines
3.6 KiB
Markdown
---
|
|
name: email-to-expense
|
|
description: |
|
|
Process emails from a Gmail label into work expenses. Opens Gmail via browser automation,
|
|
iterates through emails in the "expense" label, saves each as PDF, extracts metadata,
|
|
and logs them to MongoDB via the log-work-expense skill.
|
|
|
|
Triggers when user mentions:
|
|
- "process my expense emails"
|
|
- "email to expense"
|
|
- "triage my expense label"
|
|
- "process expense label"
|
|
---
|
|
|
|
# Email to Expense
|
|
|
|
Pulls emails from Gmail's "expense" label, saves them as PDFs, extracts metadata, and inserts them into `wip.work_expenses`.
|
|
|
|
## Prerequisites
|
|
|
|
- Browser tool available (`openclaw browser`)
|
|
- User's Gmail must be logged in (use `profile="user"`)
|
|
- `log-work-expense` skill scripts accessible at `~/notes/skills/log-work-expense/scripts/`
|
|
|
|
## Flow
|
|
|
|
### 1. Open Gmail with expense label
|
|
|
|
Open Gmail filtered to the "expense" label using the user's browser profile:
|
|
|
|
```
|
|
URL: https://mail.google.com/mail/u/0/#label/expense
|
|
```
|
|
|
|
If the label name has spaces or special characters, URL-encode it. The label may also be accessed as "2 expense" — adjust accordingly.
|
|
|
|
Take a snapshot to see how many emails are in the list. Note the email count.
|
|
|
|
### 2. Iterate through each email
|
|
|
|
For each email in the label (oldest first to maintain order):
|
|
|
|
1. **Click the email** to open it
|
|
2. **Snapshot the open email** to extract metadata:
|
|
- **Date** — from the email header (parse the displayed date string)
|
|
- **Sender** — from the "From" field
|
|
- **Subject** — from the subject line
|
|
3. **Save as PDF** — Use the browser's print-to-PDF functionality:
|
|
- Use `action="pdf"` if available, or trigger `Ctrl+P` and save to a temp directory
|
|
- Save to `/tmp/email_expense_<index>.pdf`
|
|
4. **Upload PDF to S2** — Use the S2 upload endpoint from TOOLS.md:
|
|
```bash
|
|
curl -X POST https://api.connorrhodes.com/agent/s2_upload \
|
|
-H "x-api-key: LT6CXiLT5cEApfqtThz17bENr6OLp804FepOMqa1tZkfTGXiiCcSFlupl6gaYeX" \
|
|
-F "file=@/tmp/email_expense_<index>.pdf"
|
|
```
|
|
Collect the returned S2 URL.
|
|
5. **Return to the email list** — Go back to the label view
|
|
|
|
### 3. Classify each expense
|
|
|
|
After collecting all emails, classify each one:
|
|
|
|
**Known merchants (auto-classify):**
|
|
|
|
| Sender / Keyword | Type | Default Account |
|
|
|-------------------|------|-----------------|
|
|
| DoorDash | meal | _(ask user)_ |
|
|
| Uber Eats | meal | _(ask user)_ |
|
|
| Amazon | other | _(ask user)_ |
|
|
|
|
For unknown senders, **ask the user** to classify:
|
|
- Type: `meal`, `mileage`, `other`, `professional-development`, `software`, etc.
|
|
- Account: the work account to bill against
|
|
|
|
### 4. Insert into MongoDB
|
|
|
|
For each classified email, run the existing log_expense.py script:
|
|
|
|
```bash
|
|
uv run --with pymongo ~/notes/skills/log-work-expense/scripts/log_expense.py \
|
|
<type> <account> <date> "<note>" <s2_url>
|
|
```
|
|
|
|
- **type**: meal, mileage, or other
|
|
- **account**: from classification step
|
|
- **date**: parsed from the email header (YYYY-MM-DD format)
|
|
- **note**: include sender and subject for context, e.g. "DoorDash - order confirmation"
|
|
- **s2_url**: the uploaded PDF URL
|
|
|
|
### 5. Confirm
|
|
|
|
Show a summary table of all processed expenses:
|
|
- Sender | Date | Type | Account | Status
|
|
|
|
### 6. Cleanup
|
|
|
|
- Remove local PDF files from `/tmp/`
|
|
- Optionally archive/remove the processed label from Gmail (ask user first)
|
|
|
|
## Notes
|
|
|
|
- Always ask the user to confirm before inserting expenses into the database
|
|
- If an email doesn't look like an expense (no receipt, no order confirmation, etc.), skip it and note it
|
|
- The Gmail label URL format: `https://mail.google.com/mail/u/0/#label/<labelname>`
|
|
- For labels with spaces, the URL typically uses the raw label name
|