---
name: email-with-attachment
description: "Send emails with file attachments via Outlook REST API v2.0."
whenToUse: "User asks to email a file/document/PDF/PPTX"
---

# Skill: Email with Attachment (Outlook REST API)

## Description
Send emails with file attachments via Outlook REST API v2.0. Use this when the Graph channel's `outlook_compose_email` tool doesn't support attachments.

## When to Use
- User asks to email a file/document/PDF/PPTX
- Need to send email with attachment
- Graph channel email was sent but attachment is missing

## IMPORTANT: Use Outlook REST API, NOT Graph API, NOT COM
- Our token audience is `https://outlook.office.com` (not `https://graph.microsoft.com`)
- COM `$mail.Send()` is unreliable — emails get stuck in Outbox with lost attachments
- **Outlook REST API v2.0 is the ONLY working method** ✅

## Steps

### 1. Create the File First
Generate the file (PDF, PPTX, DOCX, etc.) and save to a known path:
- Temp: system temp directory (use `tempfile.gettempdir()` in Python or `$env:TEMP` in PS)
- Desktop: `~/Desktop/{filename}` (use `os.path.expanduser('~/Desktop')` in Python)

### 2. Send via Outlook REST API v2.0
```python
import json, os, base64, urllib.request, urllib.error

# Read token
token_path = os.path.expanduser("~/.graph-token/tokens/outlook-mail.json")
with open(token_path, 'r') as f:
    token_data = json.load(f)
access_token = token_data['access_token']

# Read and encode file
with open(file_path, 'rb') as f:
    file_b64 = base64.b64encode(f.read()).decode('utf-8')

# Build email payload
email_payload = {
    "Message": {
        "Subject": subject,
        "Body": {"ContentType": "Text", "Content": body_text},
        "ToRecipients": [
            {"EmailAddress": {"Address": recipient}}
        ],
        "Attachments": [
            {
                "@odata.type": "#Microsoft.OutlookServices.FileAttachment",
                "Name": filename,
                "ContentType": content_type,  # e.g., "application/pdf"
                "ContentBytes": file_b64
            }
        ]
    },
    "SaveToSentItems": "true"
}

# Send
url = "https://outlook.office.com/api/v2.0/me/sendmail"
data = json.dumps(email_payload).encode('utf-8')
req = urllib.request.Request(url, data=data, method='POST')
req.add_header('Authorization', f'Bearer {access_token}')
req.add_header('Content-Type', 'application/json')

response = urllib.request.urlopen(req)
# Status 202 = success
```

### 3. Content Types
| File Type | Content-Type |
|-----------|-------------|
| PDF | `application/pdf` |
| DOCX | `application/vnd.openxmlformats-officedocument.wordprocessingml.document` |
| PPTX | `application/vnd.openxmlformats-officedocument.presentationml.presentation` |
| XLSX | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` |
| PNG | `image/png` |
| ZIP | `application/zip` |

### 4. For HTML Body
Change `"ContentType": "Text"` to `"ContentType": "HTML"` and use HTML in Content.

### 5. For CC/BCC
Add to the Message object:
```json
"CcRecipients": [{"EmailAddress": {"Address": "cc@microsoft.com"}}],
"BccRecipients": [{"EmailAddress": {"Address": "bcc@microsoft.com"}}]
```

### 6. Multiple Attachments
Add more items to the Attachments array.

## Important Notes
- Token is at `~/.graph-token/tokens/outlook-mail.json`
- API endpoint: `https://outlook.office.com/api/v2.0/me/sendmail`
- Status 202 = Accepted (success)
- Max attachment size: ~35MB (base64 encoded)
- **Always confirm with user before sending** (send confirmation gate)
- For self-send: use the user's own email address

## Troubleshooting
| Problem | Fix |
|---------|-----|
| 401 Invalid audience | Make sure using `outlook.office.com` NOT `graph.microsoft.com` |
| 401 Token expired | Run refresh-token skill first |
| COM Send() stuck in Outbox | Don't use COM — use this REST API method |
| Large file fails | Split into chunks or compress first |

## Verification
Check response status code: 202 = success.
