Alert Configuration Guide
Setting up threshold-based alerts via Slack, email, and SMS from n8n.
Alert Configuration Guide
Professional services firms lose revenue when utilization drops, projects go over budget, or receivables age past 60 days. You need to know about these problems before they show up in monthly reports.
This guide shows you how to build threshold-based alerts in n8n that notify you via Slack, email, or SMS when key metrics cross critical thresholds. You'll have working alerts in 30 minutes.
What You Need Before Starting
Required:
- Active n8n instance (cloud or self-hosted)
- Credentials for at least one notification channel
Recommended data sources:
- Harvest, Productive.io, or Accelo for utilization tracking
- QuickBooks Online or Xero for AR aging
- Salesforce or HubSpot for pipeline velocity
- Float or Resource Guru for capacity planning
Notification channels:
- Slack workspace with webhook permissions
- SMTP credentials for email (Gmail, Office 365, SendGrid)
- Twilio account with SMS-enabled phone number
Step 1: Build Your Data Connection
Create the workflow:
- Open n8n and click "Add workflow"
- Name it "Utilization Alert - Weekly" (use specific, searchable names)
- Click the "+" icon to add your first node
Connect to your data source:
- Search for your PSA tool node (example: "Harvest")
- Click "Create New Credential"
- Set the resource to "Time Entries" or equivalent
- Configure the filter:
- Date range: Last 7 days
- Status: Approved entries only
- Group by: User ID
Test the connection:
- Click "Execute Node"
- Verify you see time entry data in the output panel
- Note the field names - you'll reference these in your alert logic
If the test fails, check API
Step 2: Calculate Your Alert Metric
Add a "Code" node (not "Function" - Code gives you better debugging).
For utilization alerts, use this template:
// Calculate team utilization rate
const entries = $input.all();
const totalHours = entries.reduce((sum, entry) => {
return sum + (entry.json.hours || 0);
}, 0);
const workingDays = 5; // Adjust for your work week
const teamSize = 12; // Update with your actual team size
const expectedHours = workingDays * 8 * teamSize;
const utilizationRate = (totalHours / expectedHours) * 100;
// Define thresholds
const warningThreshold = 75;
const criticalThreshold = 65;
let alertLevel = null;
let alertMessage = '';
if (utilizationRate < criticalThreshold) {
alertLevel = 'critical';
alertMessage = `CRITICAL: Team utilization at ${utilizationRate.toFixed(1)}% (target: 75%+)`;
} else if (utilizationRate < warningThreshold) {
alertLevel = 'warning';
alertMessage = `WARNING: Team utilization at ${utilizationRate.toFixed(1)}% (target: 75%+)`;
}
return {
json: {
alertLevel: alertLevel,
utilizationRate: utilizationRate.toFixed(1),
totalHours: totalHours,
expectedHours: expectedHours,
message: alertMessage,
timestamp: new Date().toISOString()
}
};
For AR aging alerts:
// Flag invoices over 60 days past due
const invoices = $input.all();
const today = new Date();
const sixtyDaysAgo = new Date(today.setDate(today.getDate() - 60));
const overdueInvoices = invoices.filter(inv => {
const dueDate = new Date(inv.json.due_date);
return dueDate < sixtyDaysAgo && inv.json.status === 'open';
});
const totalOverdue = overdueInvoices.reduce((sum, inv) => {
return sum + parseFloat(inv.json.amount);
}, 0);
let alertLevel = null;
let alertMessage = '';
if (totalOverdue > 50000) {
alertLevel = 'critical';
alertMessage = `CRITICAL: ${totalOverdue.toLocaleString()} in invoices 60+ days overdue`;
} else if (totalOverdue > 25000) {
alertLevel = 'warning';
alertMessage = `WARNING: ${totalOverdue.toLocaleString()} in invoices 60+ days overdue`;
}
return {
json: {
alertLevel: alertLevel,
overdueAmount: totalOverdue,
invoiceCount: overdueInvoices.length,
message: alertMessage,
invoiceList: overdueInvoices.map(inv => ({
number: inv.json.invoice_number,
client: inv.json.client_name,
amount: inv.json.amount,
dueDate: inv.json.due_date
}))
}
};
Click "Execute Node" and verify the output contains your calculated metrics.
Step 3: Route Based on Alert Level
Add an "IF" node after your Code node.
Configure the condition:
- Set "Conditions" to "alertLevel"
- Choose "is not empty"
- This routes only when an alert should fire
Connect the "true" output to your notification nodes. Connect "false" to a "No Operation" node (this prevents errors when no alert triggers).
Step 4: Configure Slack Notifications
Add a "Slack" node connected to the IF node's "true" output.
Setup steps:
- Click "Create New Credential"
- Choose "OAuth2" authentication
- Click "Connect my account" and authorize n8n
- Select your target channel (create a dedicated "#alerts" channel)
Message configuration:
Set "Text" to this template for rich formatting:
:warning: *\{\{ $json.message \}\}*
*Metric Details:*
• Utilization Rate: \{\{ $json.utilizationRate \}\}%
• Hours Logged: \{\{ $json.totalHours \}\}
• Expected Hours: \{\{ $json.expectedHours \}\}
• Alert Level: \{\{ $json.alertLevel \}\}
*Timestamp:* \{\{ $json.timestamp \}\}
<https://yourpsa.com/reports/utilization|View Full Report>
For critical alerts, add @channel mentions:
In the "Text" field, prepend: <!channel>
Enable threaded responses:
- Add a second Slack node
- Set "Resource" to "Message"
- Set "Operation" to "Post"
- In "Thread TS", reference the first message's timestamp
- Use this for follow-up details or action items
Step 5: Configure Email Notifications
Add a "Send Email" node (also connected to IF node's "true" output).
SMTP configuration for Gmail:
- Click "Create New Credential"
- Enter these settings:
- Host: smtp.gmail.com
- Port: 587
- Secure: No (TLS)
- User: your-email@gmail.com
- Password: App-specific password (not your regular password)
For Office 365:
- Host: smtp.office365.com
- Port: 587
- User: your-email@company.com
- Password: Your Office 365 password
Email template:
Subject: [\{\{ $json.alertLevel | upper \}\}] \{\{ $json.message \}\}
Body (use HTML format):
<h2 style="color: #d32f2f;">Alert Triggered</h2>
<p><strong>\{\{ $json.message \}\}</strong></p>
<table style="border-collapse: collapse; margin: 20px 0;">
<tr>
<td style="padding: 8px; border: 1px solid #ddd;"><strong>Utilization Rate:</strong></td>
<td style="padding: 8px; border: 1px solid #ddd;">\{\{ $json.utilizationRate \}\}%</td>
</tr>
<tr>
<td style="padding: 8px; border: 1px solid #ddd;"><strong>Hours Logged:</strong></td>
<td style="padding: 8px; border: 1px solid #ddd;">\{\{ $json.totalHours \}\}</td>
</tr>
<tr>
<td style="padding: 8px; border: 1px solid #ddd;"><strong>Expected Hours:</strong></td>
<td style="padding: 8px; border: 1px solid #ddd;">\{\{ $json.expectedHours \}\}</td>
</tr>
</table>
<p><a href="https://yourpsa.com/reports/utilization" style="background: #1976d2; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px;">View Full Report</a></p>
<hr>
<p style="color: #666; font-size: 12px;">Alert generated at \{\{ $json.timestamp \}\}</p>
To email multiple recipients:
In the "To Email" field, enter comma-separated addresses: partner@firm.com, operations@firm.com
Step 6: Configure SMS Notifications
Add a "Twilio" node for critical alerts only.
Setup Twilio:
- Sign up at twilio.com (free trial includes $15 credit)
- Get a phone number (costs $1/month)
- Copy your Account SID and Auth Token from the dashboard
Configure the node:
- Click "Create New Credential"
- Enter Account SID and Auth Token
- Set "From" to your Twilio phone number
- Set "To" to the recipient's mobile number (format: +15551234567)
Message template (160 characters max):
\{\{ $json.alertLevel | upper \}\}: \{\{ $json.message \}\}. Check Slack #alerts for details.
Cost control:
SMS alerts cost $0.0075 per message. For a workflow that checks hourly, worst case is $5.40/month (24 alerts/day * 30 days * $0.0075). Add a "Limit" node before Twilio to cap at 5 SMS per day.
Step 7: Add Alert Suppression Logic
Insert a "Code" node before your IF node to prevent alert fatigue.
Suppress alerts during off-hours:
const now = new Date();
const hour = now.getHours();
const day = now.getDay();
// Suppress on weekends (0 = Sunday, 6 = Saturday)
if (day === 0 || day === 6) {
return { json: { alertLevel: null } };
}
// Suppress outside business hours (9 AM - 6 PM)
if (hour < 9 || hour >= 18) {
return { json: { alertLevel: null } };
}
// Pass through the original data
return $input.all();
Suppress duplicate alerts:
Use n8n's built-in "Merge" node with "Keep Key Matches" to check against a Google Sheet or database of recent alerts. Only fire if the same alert hasn't triggered in the last 24 hours.
Step 8: Schedule Your Workflow
Click the "Add trigger" button at the start of your workflow.
For daily checks:
- Add a "Schedule Trigger" node
- Set "Trigger Interval" to "Days"
- Set "Days Between Triggers" to 1
- Set "Trigger at Hour" to 9 (9 AM)
- Set timezone to your local timezone
For hourly checks during business hours:
- Set "Trigger Interval" to "Hours"
- Set "Hours Between Triggers" to 1
- Add the off-hours suppression code from Step 7
For real-time alerts:
Replace the Schedule Trigger with a "Webhook
Step 9: Test End-to-End
Manual test:
- Click "Execute Workflow" in the top right
- Watch each node execute in sequence
- Verify you receive notifications in all configured channels
- Check that message formatting looks correct
Adjust thresholds temporarily:
Set your alert threshold artificially high to force an alert. For example, change utilizationRate < 75 to utilizationRate < 100. This guarantees an alert fires during testing.
Test failure scenarios:
- Disconnect your data source APIcredentialsAPIClick to read the full definition in our AI & Automation Glossary.
- Execute the workflow
- Verify you receive an error notification (add an "Error Trigger" node to catch failures)
Advanced Configurations
Multi-tier escalation:
Add multiple IF nodes to route different alert levels to different channels:
- Warning: Slack only
- Critical: Slack + Email
- Emergency: Slack + Email + SMS to partners
Aggregate daily summaries:
Use a "Schedule Trigger" set to 5 PM daily. Query all alerts from the past 24 hours (store in Google Sheets or a database). Send one consolidated email with all triggered alerts.
Dynamic recipient routing:
Store an on-call schedule in Google Sheets. Use a "Google Sheets" node to look up who's on call this week. Route SMS alerts to that person's mobile number.
Alert acknowledgment:
Add a Slack button using Block Kit that marks an alert as "acknowledged". Store acknowledgments in a database. Suppress repeat alerts for acknowledged issues.
Metric trending:
Store each metric calculation in a database with a timestamp. Add a "Code" node that compares this week's value to last week's. Alert on negative trends even if thresholds aren't crossed yet.
Troubleshooting Common Issues
Alerts not firing:
Check the IF node output. If it shows "false", your metric isn't crossing the threshold. Lower the threshold temporarily to verify the notification path works.
Slack messages not formatting:
Slack requires specific markdown syntax. Use *bold* not **bold**. Use _italic_ not *italic*. Test in Slack's message formatting tool first.
Email going to spam:
Add SPF and DKIM records to your domain. Use a dedicated sending service like SendGrid instead of Gmail SMTP. Include an unsubscribe link in the footer.
Twilio SMS failing:
Verify the phone number format includes country code (+1 for US). Check your Twilio account balance. Confirm the recipient's carrier isn't blocking automated messages.
Workflow timing out:
If processing large datasets, add a "Split In Batches" node before your Code node. Process 100 records at a time instead of all at once.
Bottom Line
Threshold-based alerts turn your data into a proactive management tool. Start with one critical metric (utilization or AR aging). Get that working reliably. Then expand to additional metrics.
The key is setting thresholds that trigger action, not noise. If you're getting daily alerts that you ignore, raise the threshold. If you're surprised by problems in monthly reports, lower it.
Review and adjust your alert configurations quarterly as your business changes.

Reviewed by Revenue Institute
This guide is actively maintained and reviewed by the implementation experts at Revenue Institute. As the creators of The AI Workforce Playbook, we test and deploy these exact frameworks for professional services firms scaling without new headcount.
Revenue Institute
Need help turning this guide into reality? Revenue Institute builds and implements the AI workforce for professional services firms.