Pattern Overview
Event processing is the most common agentic pattern: something happens → AI processes it → system takes action.
The Event Processing Flow
┌─────────────────────────────────────────────────────────────┐
│ EVENT PROCESSING PATTERN │
│ │
│ 1. Event Occurs │
│ • Email arrives │
│ • User signs up │
│ • Payment completes │
│ │
│ ▼ │
│ 2. Agent Processes │
│ • Classify/Categorize │
│ • Extract information │
│ • Make decision │
│ │
│ ▼ │
│ 3. System Takes Action │
│ • Update database │
│ • Send notification │
│ • Route to team │
└─────────────────────────────────────────────────────────────┘
Common Use Cases
Customer Support: Classify tickets → Route to team
Sales: New lead → Qualify and score
Operations: Invoice received → Extract and validate
Content: Blog published → Generate social posts
Example: Email Classifier Agent
The Problem
Your company receives hundreds of emails daily. Someone manually reads each and forwards to the right team (1-2 hours/day, errors common).
What we'll build: AI agent that automatically classifies and routes incoming emails.
Architecture
Email Arrives
↓
Webhook Receiver
↓
Classifier Agent
↓
Route to Team (Sales/Support/HR)
Setup
npm install @mastra/core @ai-sdk/openai zod npm install @slack/web-api resend prisma
.env
:
OPENAI_API_KEY=sk-... SLACK_BOT_TOKEN=xoxb-... RESEND_API_KEY=re_... SALES_EMAIL=sales@company.com SUPPORT_EMAIL=support@company.com WEBHOOK_SECRET=your-secret
Email Ingestion
Webhook Receiver
// app/api/webhooks/email/route.ts import { NextResponse } from 'next/server'; import { processEmail } from '@/lib/email-processor'; export async function POST(request: Request) { const emailData = await request.json(); // Parse email const email = { id: emailData.email_id, from: emailData.from, subject: emailData.subject, body: emailData.text, receivedAt: new Date(), }; // Process async (don't block webhook) processEmail(email).catch(console.error); return NextResponse.json({ success: true }); }
Classification Logic
Prompt Design
const CLASSIFIER_PROMPT = `Classify emails into: SALES, SUPPORT, HR, or SPAM. SALES: Product inquiries, pricing, demos, partnerships SUPPORT: Technical issues, bugs, how-to questions HR: Job applications, recruiting SPAM: Marketing, solicitations Priority: HIGH (urgent/blocked), MEDIUM (standard), LOW (general) Respond with JSON: { "category": "SALES", "priority": "HIGH", "confidence": 0.95, "reasoning": "Enterprise demo request" }`;
Agent Implementation
// src/mastra/agents/email-classifier.ts import { Agent } from '@mastra/core'; import { generateObject } from 'ai'; import { openai } from '@ai-sdk/openai'; import { z } from 'zod'; const ClassificationSchema = z.object({ category: z.enum(['SALES', 'SUPPORT', 'HR', 'SPAM']), priority: z.enum(['HIGH', 'MEDIUM', 'LOW']), confidence: z.number().min(0).max(1), reasoning: z.string(), }); export async function classifyEmail(email: any) { const result = await generateObject({ model: openai('gpt-4o-mini'), schema: ClassificationSchema, prompt: `${CLASSIFIER_PROMPT} FROM: ${email.from} SUBJECT: ${email.subject} BODY: ${email.body}`, }); return result.object; }
Action Handler
Email Processor
// lib/email-processor.ts import { classifyEmail } from '@/mastra/agents/email-classifier'; import { forwardEmail, createTicket, sendSlack } from '@/mastra/tools'; export async function processEmail(email: any) { // Classify const classification = await classifyEmail(email); // Skip spam if (classification.category === 'SPAM') return; // Route based on category switch (classification.category) { case 'SALES': await forwardEmail(email, process.env.SALES_EMAIL!); if (classification.priority === 'HIGH') { await sendSlack('#sales-alerts', `New high-priority lead from ${email.from}`); } break; case 'SUPPORT': const ticket = await createTicket(email, classification.priority); if (classification.priority === 'HIGH') { await sendSlack('#support-urgent', `Urgent ticket #${ticket.id}`); } break; case 'HR': await forwardEmail(email, process.env.HR_EMAIL!); break; } }
Tools
// src/mastra/tools/forward-email.ts import { Resend } from 'resend'; const resend = new Resend(process.env.RESEND_API_KEY); export async function forwardEmail(email: any, to: string) { await resend.emails.send({ from: 'classifier@company.com', to, subject: `[Classified] ${email.subject}`, text: `From: ${email.from}\n\n${email.body}`, }); } // src/mastra/tools/send-slack.ts import { WebClient } from '@slack/web-api'; const slack = new WebClient(process.env.SLACK_BOT_TOKEN); export async function sendSlack(channel: string, text: string) { await slack.chat.postMessage({ channel, text }); } // src/mastra/tools/create-ticket.ts export async function createTicket(email: any, priority: string) { // Your ticketing system integration return { id: '12345' }; }
Testing
// tests/classifier.test.ts import { classifyEmail } from '@/mastra/agents/email-classifier'; test('classifies sales inquiry', async () => { const email = { from: 'john@acme.com', subject: 'Demo request', body: 'Interested in enterprise plan', }; const result = await classifyEmail(email); expect(result.category).toBe('SALES'); }); test('detects urgent support', async () => { const email = { from: 'customer@example.com', subject: 'URGENT: Cannot login', body: 'Blocked, need help ASAP!', }; const result = await classifyEmail(email); expect(result.category).toBe('SUPPORT'); expect(result.priority).toBe('HIGH'); });
Deployment
Vercel
vercel --prod vercel env add OPENAI_API_KEY vercel env add SLACK_BOT_TOKEN
Email Provider Setup
Configure your email provider (SendGrid, Gmail, etc.) to send webhooks to:
https://your-app.vercel.app/api/webhooks/email
Key Takeaways
- Event processing is simple - Receive → Process → Act
- Use structured outputs -
generateObject
for reliable JSON - Process async - Don't block webhooks
- Route intelligently - Different actions for different categories
- Monitor everything - Log classifications and errors
What's Next
- Pattern 2: External Monitoring - RSS feeds, competitor tracking
- Pattern 3: Event Preparation - Pre-event briefings
- Pattern 4: Scheduled Analysis - Daily/weekly reports
Get chapter updates & code samples
We’ll email diagrams, code snippets, and additions.