An intelligent notification routing system that receives webhooks from various sources (Linear, Sentry, etc.) and uses AI to determine the most appropriate channels for delivery. The system analyzes notification content and routes messages to predefined email channels based on context, priority, and team relevance.
Instead of broadcasting all notifications to everyone, this system:
- Receives webhooks from external services
- Analyzes the content using AI (GPT-4)
- Routes notifications to appropriate channels based on:
- Content analysis and keywords
- Priority/severity levels
- Team assignments
- Predefined channel descriptions
- Delivers formatted, actionable messages via email
Each notification includes:
- Clear summary of what happened
- Relevant links in proper format
- Context tailored to the target audience
- Action items when applicable
This system is built around the principle of AI transparency and debuggability. We use structured data formatting and comprehensive logging to make AI decision-making visible and debuggable.
We use the @zenbase/llml
library to convert JavaScript objects into clean, XML-like formatted strings. This approach provides several benefits:
- Human-readable logs: Complex webhook payloads become easy to scan and understand
- AI-friendly format: Structured data that AI models can easily parse and reason about
- Consistent formatting: All logged objects follow the same clear structure
- Debugging efficiency: Quickly identify issues in webhook data or AI responses
Our formatting utilities provide:
// Convert any object to formatted XML-like string
const formatted = stringifyForLogging(webhookPayload);
// Log with clear separators and labels
logFormatted(payload, "Linear Webhook Received");
// Handle large objects with truncation
const truncated = stringifyTruncated(largeObject, 1000);
Key Functions:
stringifyForLogging(obj)
- Primary formatter using llml with JSON fallbacklogFormatted(obj, label)
- Adds clear separators and labels for log scanningstringifyTruncated(obj, maxLength)
- Prevents log overflow with large payloads
Why This Matters:
- AI Debugging: When AI makes routing decisions, we can see exactly what data it analyzed
- Webhook Debugging: Complex nested webhook payloads become immediately readable
- Error Tracking: Failed AI responses are logged in structured format for analysis
- Performance Monitoring: Easy to spot patterns in notification types and routing decisions
This structured approach to logging and data formatting makes the entire AI pipeline transparent and debuggable, which is crucial for a system that makes automated decisions about important notifications.
├── CONFIGURATION.ts # Channel definitions, webhook config, AI prompts
├── citation-context.ts # Citation utilities for AI prompting with links
├── linear-webhook.ts # HTTP webhook handler for Linear (with GET config page)
├── notification-triage.ts # Shared AI triage system for all notification sources
├── stringify-utils.ts # LLML-based object formatting utilities for AI debugging
├── main.tsx # (Reserved for future integrations)
└── README.md # This file
- Webhook Handler: Secure endpoint with HMAC-SHA256 signature verification
- Configuration Page: Visit the webhook URL in browser for setup instructions
- Event Filtering: Skips low-value events (views, reads)
- Rich Data Processing: Handles titles, descriptions, assignees, labels, priorities, teams
- Unified Interface: All webhook sources convert to standardized
NotificationData
format - Citation Context: Advanced AI prompting with proper link generation and ID replacement
- Fallback Logic: Intelligent routing even when AI fails, based on source and priority
- Enhanced Metadata: Tracks urgency, suggested actions, and formatted summaries
- Link Generation: Automatic creation of proper URLs and citations in AI responses
- ID Replacement: Converts UUIDs and IDs to readable citation keys for better AI understanding
- Structured References: Maintains mapping between citation keys and actual resources
- Markdown Output: Generates properly formatted links in final notifications
- Multi-Channel Support: Can notify multiple channels for critical issues
- Rich Formatting: HTML emails with structured information
- Branded Messages: Consistent formatting with clear subject prefixes
- Error Handling: Graceful failure handling per channel
Pre-configured channels include:
- Engineering Critical: P0/P1 issues, outages, security incidents
- Engineering General: Feature development, code reviews, technical debt
- Product Team: UX issues, feature requests, business logic
- DevOps & Infrastructure: Deployment, monitoring, performance
- Quality Assurance: Testing, automation, bug reports
- General Notifications: Low-priority and uncategorized items
Set these in your Val Town environment:
LINEAR_WEBHOOK_SECRET
- Your Linear webhook signing secretOPENAI_API_KEY
- Automatically handled by Val Town's OpenAI integration
Update email addresses in CONFIGURATION.ts
to match your organization:
{
id: 'engineering-critical',
name: 'Engineering Critical',
email: 'your-critical-team@company.com', // Update this
description: '...',
// ...
}
- Visit your webhook URL in a browser to see configuration instructions
- Copy the webhook URL from the configuration page
- In Linear: Settings → API → Webhooks → Create webhook
- Paste the URL and copy the signing secret
- Add the signing secret to Val Town as
LINEAR_WEBHOOK_SECRET
- Select events to monitor (Issues, Comments, Projects recommended)
Create or update a Linear issue to test the integration. Check logs in Val Town for debugging.
- Receive: Linear sends webhook with HMAC signature
- Verify: Validate signature using signing secret
- Parse: Extract relevant data (title, description, priority, labels, etc.)
- Analyze: Send to GPT-4 with channel descriptions for triage
- Route: AI selects appropriate channel(s) with reasoning
- Format: Create tailored message for target audience
- Deliver: Send email notification(s)
- Log: Record success/failure for debugging
The AI considers:
- Keywords: Matches against channel-specific keywords
- Priority: Routes high-priority items to critical channels
- Team Context: Considers team assignments and technical domains
- Content Analysis: Analyzes descriptions for technical vs. product issues
- Multi-Channel Logic: Can notify multiple channels for critical issues
Each channel has:
- Description: Detailed explanation of what belongs there
- Keywords: Specific terms that indicate relevance
- Priority Level: High/medium/low for urgency-based routing
- Email Address: Delivery target
- Sentry: Error monitoring and performance alerts
- GitHub: PR reviews, security alerts, deployment status
- Slack: Direct Slack channel delivery (alternative to email)
- PagerDuty: Incident management integration
- Smart Scheduling: Respect time zones and on-call schedules
- Escalation Logic: Auto-escalate if no response within timeframe
- Analytics Dashboard: Track notification patterns and effectiveness
- Custom Rules: User-defined routing rules beyond AI
- Digest Mode: Batch low-priority notifications
When adding new integrations:
- Create a new webhook handler file (e.g.,
sentry-webhook.ts
) - Convert the source payload to
NotificationData
format in your handler - Call
processNotification(notificationData)
from the shared triage system - Include GET endpoint for configuration instructions
- Add any source-specific configuration to
CONFIGURATION.ts
- Update this README with new features
The shared triage system expects all notifications in this standardized format:
interface NotificationData {
source: string; // e.g., 'linear', 'sentry', 'billing'
id: string; // unique identifier
type: string; // e.g., 'issue', 'error', 'payment_failed'
action: string; // e.g., 'created', 'updated', 'resolved'
title: string;
description?: string;
priority?: number | string;
severity?: 'low' | 'medium' | 'high' | 'critical';
labels?: string[];
team?: string;
assignee?: { name: string; email?: string };
url?: string;
// ... additional fields
}
The system will automatically:
- Generate citation contexts for better AI prompting
- Create proper links in notifications
- Apply fallback routing logic
- Format emails consistently across all sources
- Webhook Logs: Check Val Town logs for processing details
- Structured Logging: All webhook payloads and AI responses are logged using LLML formatting for easy reading
- AI Transparency: Triage reasoning is logged with clear separators (e.g., "=== AI Triage Result ===")
- Email Delivery: Individual channel delivery status is logged
- Signature Verification: Failed authentications are logged with details
- LLML Formatting: Complex objects are automatically formatted as readable XML-like structures
=== Linear Webhook Payload ===
<LinearWebhookPayload>
<action>create</action>
<type>Issue</type>
<data>
<title>Critical payment bug</title>
<priority>1</priority>
<labels>
<item><name>critical</name></item>
</labels>
</data>
</LinearWebhookPayload>
=== End Linear Webhook Payload ===
=== AI Triage Result ===
<TriageResult>
<selectedChannels>
<item>engineering-critical</item>
</selectedChannels>
<reasoning>High priority payment issue requires immediate engineering attention</reasoning>
</TriageResult>
=== End AI Triage Result ===