Initial commit of OpenClaw agent Chloe

This commit is contained in:
Chloe Bot
2026-03-02 20:22:49 +00:00
commit 80e697f8d9
67 changed files with 16100 additions and 0 deletions

View File

@@ -0,0 +1,230 @@
# AgentMail API Reference
Base URL: `https://api.agentmail.to/v0`
## Authentication
All requests require Bearer token authentication:
```
Authorization: Bearer YOUR_API_KEY
```
## Inboxes
### Create Inbox
```http
POST /v0/inboxes
```
**Request:**
```json
{
"username": "my-agent", // Optional: custom username
"domain": "agentmail.to", // Optional: defaults to agentmail.to
"display_name": "My Agent", // Optional: friendly name
"client_id": "unique-id" // Optional: for idempotency
}
```
**Response:**
```json
{
"pod_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"inbox_id": "my-agent@agentmail.to",
"display_name": "My Agent",
"created_at": "2024-01-10T08:15:00Z",
"updated_at": "2024-01-10T08:15:00Z",
"client_id": "unique-id"
}
```
### List Inboxes
```http
GET /v0/inboxes?limit=10&page_token=eyJwYWdlIjoxfQ==
```
**Response:**
```json
{
"count": 2,
"inboxes": [...],
"limit": 10,
"next_page_token": "eyJwYWdlIjoyMQ=="
}
```
### Get Inbox
```http
GET /v0/inboxes/{inbox_id}
```
## Messages
### Send Message
```http
POST /v0/inboxes/{inbox_id}/messages
```
**Request:**
```json
{
"to": ["recipient@example.com"], // Required: string or array
"cc": ["cc@example.com"], // Optional: string or array
"bcc": ["bcc@example.com"], // Optional: string or array
"reply_to": "reply@example.com", // Optional: string or array
"subject": "Email subject", // Optional: string
"text": "Plain text body", // Optional: string
"html": "<p>HTML body</p>", // Optional: string
"labels": ["sent", "important"], // Optional: array
"attachments": [{ // Optional: array of objects
"filename": "document.pdf",
"content": "base64-encoded-content",
"content_type": "application/pdf"
}],
"headers": { // Optional: custom headers
"X-Custom-Header": "value"
}
}
```
**Response:**
```json
{
"message_id": "msg_123abc",
"thread_id": "thd_789ghi"
}
```
### List Messages
```http
GET /v0/inboxes/{inbox_id}/messages?limit=10&page_token=token
```
### Get Message
```http
GET /v0/inboxes/{inbox_id}/messages/{message_id}
```
## Threads
### List Threads
```http
GET /v0/inboxes/{inbox_id}/threads?limit=10
```
### Get Thread
```http
GET /v0/inboxes/{inbox_id}/threads/{thread_id}
```
**Response:**
```json
{
"thread_id": "thd_789ghi",
"inbox_id": "support@example.com",
"subject": "Question about my account",
"participants": ["jane@example.com", "support@example.com"],
"labels": ["customer-support"],
"message_count": 3,
"last_message_at": "2023-10-27T14:30:00Z",
"created_at": "2023-10-27T10:00:00Z",
"updated_at": "2023-10-27T14:30:00Z"
}
```
## Webhooks
### Create Webhook
```http
POST /v0/webhooks
```
**Request:**
```json
{
"url": "https://your-domain.com/webhook",
"client_id": "webhook-identifier",
"enabled": true,
"event_types": ["message.received"], // Optional: defaults to all events
"inbox_ids": ["inbox1@domain.com"] // Optional: filter by specific inboxes
}
```
### List Webhooks
```http
GET /v0/webhooks
```
### Update Webhook
```http
PUT /v0/webhooks/{webhook_id}
```
### Delete Webhook
```http
DELETE /v0/webhooks/{webhook_id}
```
## Error Responses
All errors follow this format:
```json
{
"error": {
"type": "validation_error",
"message": "Invalid email address",
"details": {
"field": "to",
"code": "INVALID_EMAIL"
}
}
}
```
Common error codes:
- `400` - Bad Request (validation errors)
- `401` - Unauthorized (invalid API key)
- `404` - Not Found (resource doesn't exist)
- `429` - Too Many Requests (rate limited)
- `500` - Internal Server Error
## Rate Limits
AgentMail is designed for high-volume use with generous limits:
- API requests: 1000/minute per API key
- Email sending: 10,000/day (upgradeable)
- Webhook deliveries: Real-time, no limits
## Python SDK
The Python SDK provides a convenient wrapper around the REST API:
```python
from agentmail import AgentMail
import os
client = AgentMail(api_key=os.getenv("AGENTMAIL_API_KEY"))
# All operations return structured objects
inbox = client.inboxes.create(username="my-agent")
message = client.inboxes.messages.send(
inbox_id=inbox.inbox_id,
to="user@example.com",
subject="Hello",
text="Message body"
)
```

View File

@@ -0,0 +1,509 @@
# AgentMail Usage Examples
Common patterns and use cases for AgentMail in AI agent workflows.
## Basic Agent Email Setup
### 1. Create Agent Identity
```python
from agentmail import AgentMail
import os
client = AgentMail(api_key=os.getenv("AGENTMAIL_API_KEY"))
# Create inbox for your agent
agent_inbox = client.inboxes.create(
username="spike-assistant",
display_name="Spike - AI Assistant",
client_id="spike-main-inbox" # Prevents duplicates
)
print(f"Agent email: {agent_inbox.inbox_id}")
# Output: spike-assistant@agentmail.to
```
### 2. Send Status Updates
```python
def send_task_completion(task_name, details, recipient):
client.inboxes.messages.send(
inbox_id="spike-assistant@agentmail.to",
to=recipient,
subject=f"Task Completed: {task_name}",
text=f"Hello! I've completed the task: {task_name}\n\nDetails:\n{details}\n\nBest regards,\nSpike 🦝",
html=f"""
<p>Hello!</p>
<p>I've completed the task: <strong>{task_name}</strong></p>
<h3>Details:</h3>
<p>{details.replace(chr(10), '<br>')}</p>
<p>Best regards,<br>Spike 🦝</p>
"""
)
# Usage
send_task_completion(
"PDF Processing",
"Rotated 5 pages, extracted text, and saved output to /tmp/processed.pdf",
"adam@example.com"
)
```
## Customer Support Automation
### Auto-Reply System
```python
def setup_support_auto_reply():
"""Set up webhook to auto-reply to support emails"""
# Create support inbox
support_inbox = client.inboxes.create(
username="support",
display_name="Customer Support",
client_id="support-inbox"
)
# Register webhook for auto-replies
webhook = client.webhooks.create(
url="https://your-app.com/webhook/support",
event_types=["message.received"],
inbox_ids=[support_inbox.inbox_id],
client_id="support-webhook"
)
return support_inbox, webhook
def handle_support_message(message):
"""Process incoming support message and send auto-reply"""
subject = message['subject'].lower()
sender = message['from'][0]['email']
# Determine response based on subject keywords
if 'billing' in subject or 'payment' in subject:
response = """
Thank you for your billing inquiry.
Our billing team will review your request and respond within 24 hours.
For urgent billing issues, please call 1-800-SUPPORT.
Best regards,
Customer Support Team
"""
elif 'bug' in subject or 'error' in subject:
response = """
Thank you for reporting this issue.
Our technical team has been notified and will investigate.
We'll update you within 48 hours with our findings.
If you have additional details, please reply to this email.
Best regards,
Technical Support
"""
else:
response = """
Thank you for contacting us!
We've received your message and will respond within 24 hours.
For urgent issues, please call our support line.
Best regards,
Customer Support Team
"""
# Send auto-reply
client.inboxes.messages.send(
inbox_id=message['inbox_id'],
to=sender,
subject=f"Re: {message['subject']}",
text=response
)
# Log for human follow-up
print(f"Auto-replied to {sender} about: {message['subject']}")
```
## Document Processing Workflow
### Email → Process → Reply
```python
import base64
import tempfile
from pathlib import Path
def process_pdf_attachment(message):
"""Extract attachments, process PDFs, and reply with results"""
processed_files = []
for attachment in message.get('attachments', []):
if attachment['content_type'] == 'application/pdf':
# Decode attachment
pdf_data = base64.b64decode(attachment['content'])
# Save to temp file
with tempfile.NamedTemporaryFile(suffix='.pdf', delete=False) as tmp:
tmp.write(pdf_data)
temp_path = tmp.name
try:
# Process PDF (example: extract text)
extracted_text = extract_pdf_text(temp_path)
# Save processed result
output_path = f"/tmp/processed_{attachment['filename']}.txt"
with open(output_path, 'w') as f:
f.write(extracted_text)
processed_files.append({
'original': attachment['filename'],
'output': output_path,
'preview': extracted_text[:200] + '...'
})
finally:
Path(temp_path).unlink() # Clean up temp file
if processed_files:
# Send results back
results_text = "\n".join([
f"Processed {f['original']}:\n{f['preview']}\n"
for f in processed_files
])
# Attach processed files
attachments = []
for f in processed_files:
with open(f['output'], 'r') as file:
content = base64.b64encode(file.read().encode()).decode()
attachments.append({
'filename': Path(f['output']).name,
'content': content,
'content_type': 'text/plain'
})
client.inboxes.messages.send(
inbox_id=message['inbox_id'],
to=message['from'][0]['email'],
subject=f"Re: {message['subject']} - Processed",
text=f"I've processed your PDF files:\n\n{results_text}",
attachments=attachments
)
def extract_pdf_text(pdf_path):
"""Extract text from PDF file"""
# Implementation depends on your PDF library
# Example with pdfplumber:
import pdfplumber
text = ""
with pdfplumber.open(pdf_path) as pdf:
for page in pdf.pages:
text += page.extract_text() + "\n"
return text
```
## Task Assignment and Tracking
### Email-Based Task Management
```python
def create_task_tracker_inbox():
"""Set up inbox for task assignments via email"""
inbox = client.inboxes.create(
username="tasks",
display_name="Task Assignment Bot",
client_id="task-tracker"
)
# Webhook for processing task emails
webhook = client.webhooks.create(
url="https://your-app.com/webhook/tasks",
event_types=["message.received"],
inbox_ids=[inbox.inbox_id]
)
return inbox
def process_task_assignment(message):
"""Parse email and create task from content"""
subject = message['subject']
body = message.get('text', '')
sender = message['from'][0]['email']
# Simple task parsing
if subject.startswith('TASK:'):
task_title = subject[5:].strip()
# Extract due date, priority, etc. from body
lines = body.split('\n')
due_date = None
priority = 'normal'
description = body
for line in lines:
if line.startswith('Due:'):
due_date = line[4:].strip()
elif line.startswith('Priority:'):
priority = line[9:].strip().lower()
# Create task in your system
task_id = create_task_in_system({
'title': task_title,
'description': description,
'due_date': due_date,
'priority': priority,
'assigned_by': sender
})
# Confirm task creation
client.inboxes.messages.send(
inbox_id=message['inbox_id'],
to=sender,
subject=f"Task Created: {task_title} (#{task_id})",
text=f"""
Task successfully created!
ID: #{task_id}
Title: {task_title}
Priority: {priority}
Due: {due_date or 'Not specified'}
I'll send updates as work progresses.
Best regards,
Task Bot
"""
)
# Start processing task...
process_task_async(task_id)
def create_task_in_system(task_data):
"""Create task in your task management system"""
# Implementation depends on your system
# Return task ID
return "T-12345"
def send_task_update(task_id, status, details, assignee_email):
"""Send task progress update"""
client.inboxes.messages.send(
inbox_id="tasks@agentmail.to",
to=assignee_email,
subject=f"Task Update: #{task_id} - {status}",
text=f"""
Task #{task_id} Status Update
Status: {status}
Details: {details}
View full details: https://your-app.com/tasks/{task_id}
Best regards,
Task Bot
"""
)
```
## Integration with External Services
### GitHub Issue Creation from Email
```python
def setup_github_integration():
"""Create inbox for GitHub issue creation"""
inbox = client.inboxes.create(
username="github-issues",
display_name="GitHub Issue Creator",
client_id="github-integration"
)
return inbox
def create_github_issue_from_email(message):
"""Convert email to GitHub issue"""
import requests
# Extract issue details
title = message['subject'].replace('BUG:', '').replace('FEATURE:', '').strip()
body_content = message.get('text', '')
sender = message['from'][0]['email']
# Determine issue type and labels
labels = ['email-created']
if 'BUG:' in message['subject']:
labels.append('bug')
elif 'FEATURE:' in message['subject']:
labels.append('enhancement')
# Create GitHub issue
github_token = os.getenv('GITHUB_TOKEN')
repo = 'your-org/your-repo'
issue_data = {
'title': title,
'body': f"""
**Reported via email by:** {sender}
**Original message:**
{body_content}
**Email Thread:** {message.get('thread_id')}
""",
'labels': labels
}
response = requests.post(
f'https://api.github.com/repos/{repo}/issues',
json=issue_data,
headers={
'Authorization': f'token {github_token}',
'Accept': 'application/vnd.github.v3+json'
}
)
if response.status_code == 201:
issue = response.json()
# Reply with GitHub issue link
client.inboxes.messages.send(
inbox_id=message['inbox_id'],
to=sender,
subject=f"Re: {message['subject']} - GitHub Issue Created",
text=f"""
Thank you for your report!
I've created a GitHub issue for tracking:
Issue #{issue['number']}: {issue['title']}
Link: {issue['html_url']}
You can track progress and add comments directly on GitHub.
Best regards,
GitHub Bot
"""
)
print(f"Created GitHub issue #{issue['number']} from email")
else:
print(f"Failed to create GitHub issue: {response.text}")
# Usage in webhook handler
def handle_github_webhook(payload):
if payload['event_type'] == 'message.received':
message = payload['message']
if message['inbox_id'] == 'github-issues@agentmail.to':
create_github_issue_from_email(message)
```
## Notification and Alert System
### Multi-Channel Alerts
```python
def setup_alert_system():
"""Create alert inbox for system notifications"""
alerts_inbox = client.inboxes.create(
username="alerts",
display_name="System Alerts",
client_id="alert-system"
)
return alerts_inbox
def send_system_alert(alert_type, message, severity='info', recipients=None):
"""Send system alert via email"""
if recipients is None:
recipients = ['admin@company.com', 'ops@company.com']
severity_emoji = {
'critical': '🚨',
'warning': '⚠️',
'info': '',
'success': ''
}
emoji = severity_emoji.get(severity, '')
client.inboxes.messages.send(
inbox_id="alerts@agentmail.to",
to=recipients,
subject=f"{emoji} [{severity.upper()}] {alert_type}",
text=f"""
System Alert
Type: {alert_type}
Severity: {severity}
Time: {datetime.now().isoformat()}
Message:
{message}
This is an automated alert from the monitoring system.
""",
html=f"""
<h2>{emoji} System Alert</h2>
<table>
<tr><td><strong>Type:</strong></td><td>{alert_type}</td></tr>
<tr><td><strong>Severity:</strong></td><td style="color: {'red' if severity == 'critical' else 'orange' if severity == 'warning' else 'blue'}">{severity}</td></tr>
<tr><td><strong>Time:</strong></td><td>{datetime.now().isoformat()}</td></tr>
</table>
<h3>Message:</h3>
<p>{message.replace(chr(10), '<br>')}</p>
<p><em>This is an automated alert from the monitoring system.</em></p>
"""
)
# Usage examples
send_system_alert("Database Connection", "Unable to connect to primary database", "critical")
send_system_alert("Backup Complete", "Daily backup completed successfully", "success")
send_system_alert("High CPU Usage", "CPU usage above 80% for 5 minutes", "warning")
```
## Testing and Development
### Local Development Setup
```python
def setup_dev_environment():
"""Set up AgentMail for local development"""
# Create development inboxes
dev_inbox = client.inboxes.create(
username="dev-test",
display_name="Development Testing",
client_id="dev-testing"
)
print(f"Development inbox: {dev_inbox.inbox_id}")
print("Use this for testing email workflows locally")
# Test email sending
test_response = client.inboxes.messages.send(
inbox_id=dev_inbox.inbox_id,
to="your-personal-email@gmail.com",
subject="AgentMail Development Test",
text="This is a test email from your AgentMail development setup."
)
print(f"Test email sent: {test_response.message_id}")
return dev_inbox
# Run development setup
if __name__ == "__main__":
setup_dev_environment()
```

View File

@@ -0,0 +1,295 @@
# AgentMail Webhooks Guide
Webhooks enable real-time, event-driven email processing. When events occur (like receiving a message), AgentMail immediately sends a POST request to your registered endpoint.
## Event Types
### message.received
Triggered when a new email arrives. Contains full message and thread data.
**Use case:** Auto-reply to support emails, process attachments, route messages
```json
{
"type": "event",
"event_type": "message.received",
"event_id": "evt_123abc",
"message": {
"inbox_id": "support@agentmail.to",
"thread_id": "thd_789ghi",
"message_id": "msg_123abc",
"from": [{"name": "Jane Doe", "email": "jane@example.com"}],
"to": [{"name": "Support", "email": "support@agentmail.to"}],
"subject": "Question about my account",
"text": "I need help with...",
"html": "<p>I need help with...</p>",
"timestamp": "2023-10-27T10:00:00Z",
"labels": ["received"]
},
"thread": {
"thread_id": "thd_789ghi",
"subject": "Question about my account",
"participants": ["jane@example.com", "support@agentmail.to"],
"message_count": 1
}
}
```
### message.sent
Triggered when you successfully send a message.
```json
{
"type": "event",
"event_type": "message.sent",
"event_id": "evt_456def",
"send": {
"inbox_id": "support@agentmail.to",
"thread_id": "thd_789ghi",
"message_id": "msg_456def",
"timestamp": "2023-10-27T10:05:00Z",
"recipients": ["jane@example.com"]
}
}
```
### message.delivered
Triggered when your message reaches the recipient's mail server.
### message.bounced
Triggered when a message fails to deliver.
```json
{
"type": "event",
"event_type": "message.bounced",
"bounce": {
"type": "Permanent",
"sub_type": "General",
"recipients": [{"address": "invalid@example.com", "status": "bounced"}]
}
}
```
### message.complained
Triggered when recipients mark your message as spam.
## Local Development Setup
### Step 1: Install Dependencies
```bash
pip install agentmail flask ngrok python-dotenv
```
### Step 2: Set up ngrok
1. Create account at [ngrok.com](https://ngrok.com/)
2. Install: `brew install ngrok` (macOS) or download from website
3. Authenticate: `ngrok config add-authtoken YOUR_AUTHTOKEN`
### Step 3: Create Webhook Receiver
Create `webhook_receiver.py`:
```python
from flask import Flask, request, Response
import json
from agentmail import AgentMail
import os
app = Flask(__name__)
client = AgentMail(api_key=os.getenv("AGENTMAIL_API_KEY"))
@app.route('/webhook', methods=['POST'])
def handle_webhook():
payload = request.json
if payload['event_type'] == 'message.received':
message = payload['message']
# Auto-reply example
response_text = f"Thanks for your email about '{message['subject']}'. We'll get back to you soon!"
client.inboxes.messages.send(
inbox_id=message['inbox_id'],
to=message['from'][0]['email'],
subject=f"Re: {message['subject']}",
text=response_text
)
print(f"Auto-replied to {message['from'][0]['email']}")
return Response(status=200)
if __name__ == '__main__':
app.run(port=3000)
```
### Step 4: Start Services
Terminal 1 - Start ngrok:
```bash
ngrok http 3000
```
Copy the forwarding URL (e.g., `https://abc123.ngrok-free.app`)
Terminal 2 - Start webhook receiver:
```bash
python webhook_receiver.py
```
### Step 5: Register Webhook
```python
from agentmail import AgentMail
client = AgentMail(api_key="your_api_key")
webhook = client.webhooks.create(
url="https://abc123.ngrok-free.app/webhook",
client_id="dev-webhook"
)
```
### Step 6: Test
Send an email to your AgentMail inbox and watch the console output.
## Production Deployment
### Webhook Verification
Verify incoming webhooks are from AgentMail:
```python
import hmac
import hashlib
def verify_webhook(payload, signature, secret):
expected = hmac.new(
secret.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
@app.route('/webhook', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-AgentMail-Signature')
if not verify_webhook(request.data.decode(), signature, webhook_secret):
return Response(status=401)
# Process webhook...
```
### Error Handling
Return 200 status quickly, process in background:
```python
from threading import Thread
import time
def process_webhook_async(payload):
try:
# Heavy processing here
time.sleep(5) # Simulate work
handle_message(payload)
except Exception as e:
print(f"Webhook processing error: {e}")
# Log to error tracking service
@app.route('/webhook', methods=['POST'])
def handle_webhook():
payload = request.json
# Return 200 immediately
Thread(target=process_webhook_async, args=(payload,)).start()
return Response(status=200)
```
### Retry Logic
AgentMail retries failed webhooks with exponential backoff. Handle idempotency:
```python
processed_events = set()
@app.route('/webhook', methods=['POST'])
def handle_webhook():
event_id = request.json['event_id']
if event_id in processed_events:
return Response(status=200) # Already processed
# Process event...
processed_events.add(event_id)
return Response(status=200)
```
## Common Patterns
### Auto-Reply Bot
```python
def handle_message_received(message):
if 'support' in message['to'][0]['email']:
# Support auto-reply
reply_text = "Thanks for contacting support! We'll respond within 24 hours."
elif 'sales' in message['to'][0]['email']:
# Sales auto-reply
reply_text = "Thanks for your interest! A sales rep will contact you soon."
else:
return
client.inboxes.messages.send(
inbox_id=message['inbox_id'],
to=message['from'][0]['email'],
subject=f"Re: {message['subject']}",
text=reply_text
)
```
### Message Routing
```python
def route_message(message):
subject = message['subject'].lower()
if 'billing' in subject or 'payment' in subject:
forward_to_slack('#billing-team', message)
elif 'bug' in subject or 'error' in subject:
create_github_issue(message)
elif 'feature' in subject:
add_to_feature_requests(message)
```
### Attachment Processing
```python
def process_attachments(message):
for attachment in message.get('attachments', []):
if attachment['content_type'] == 'application/pdf':
# Process PDF
pdf_content = base64.b64decode(attachment['content'])
text = extract_pdf_text(pdf_content)
# Reply with extracted text
client.inboxes.messages.send(
inbox_id=message['inbox_id'],
to=message['from'][0]['email'],
subject=f"Re: {message['subject']} - PDF processed",
text=f"I extracted this text from your PDF:\n\n{text}"
)
```
## Webhook Security
- **Always verify signatures** in production
- **Use HTTPS endpoints** only
- **Validate payload structure** before processing
- **Implement rate limiting** to prevent abuse
- **Return 200 quickly** to avoid retries