# 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": "

I need help with...

", "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