This workflow follows the Emailsend → HTTP Request recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "WF-005 Overage Warning",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 9 * * *"
}
]
}
},
"id": "wf005-node-schedule",
"name": "Daily 9AM ET",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
0,
300
]
},
{
"parameters": {
"method": "GET",
"url": "https://yepggeqhtspgzlkhqhgo.supabase.co/rest/v1/usage_monthly",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "supabaseApi",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "year_month",
"value": "={{ 'eq.' + new Date().getFullYear() + '-' + String(new Date().getMonth() + 1).padStart(2, '0') }}"
},
{
"name": "select",
"value": "*, tenants:tenant_id(id, company_name, plan, plan_limit), owner:tenant_id(users!inner(email, role))"
},
{
"name": "or",
"value": "(total_minutes.gte.plan_limit*0.8)"
}
]
}
},
"id": "wf005-node-get-approaching",
"name": "Get Tenants Approaching Limit",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
250,
300
],
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Query usage_monthly joined with tenants and users via Supabase RPC or view\n// Since the join above may not work directly, we prepare the data here\nconst items = $input.all();\n\n// Filter to only those at or above 80% of plan limit\nconst results = items\n .map(i => i.json)\n .filter(row => {\n const planLimit = row.plan_limit || (row.tenants && row.tenants.plan_limit) || 0;\n const totalMinutes = row.total_minutes || 0;\n return planLimit > 0 && totalMinutes >= planLimit * 0.8;\n });\n\nif (results.length === 0) {\n return [{ json: { _empty: true, count: 0 } }];\n}\n\nreturn results.map(row => {\n const planLimit = row.plan_limit || (row.tenants && row.tenants.plan_limit) || 0;\n const totalMinutes = row.total_minutes || 0;\n const usagePercent = planLimit > 0 ? Math.round((totalMinutes / planLimit) * 100) : 0;\n const threshold = usagePercent >= 100 ? '100' : '80';\n \n return {\n json: {\n tenant_id: row.tenant_id,\n company_name: row.company_name || (row.tenants && row.tenants.company_name) || 'Customer',\n plan: row.plan || (row.tenants && row.tenants.plan) || 'Standard',\n email: row.email || (row.owner && row.owner.users && row.owner.users[0] && row.owner.users[0].email) || '',\n total_minutes: totalMinutes,\n plan_limit: planLimit,\n usage_percent: usagePercent,\n threshold: threshold,\n year_month: row.year_month\n }\n };\n});"
},
"id": "wf005-node-filter-compute",
"name": "Filter & Compute Thresholds",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
500,
300
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "wf005-cond-has-results",
"leftValue": "={{ $json._empty }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "notEqual"
}
}
],
"combinator": "and"
}
},
"id": "wf005-node-if-results",
"name": "Has Results?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
750,
300
]
},
{
"parameters": {
"batchSize": 1,
"options": {}
},
"id": "wf005-node-loop",
"name": "Loop Over Tenants",
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
1000,
300
]
},
{
"parameters": {
"method": "GET",
"url": "https://yepggeqhtspgzlkhqhgo.supabase.co/rest/v1/notifications",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "supabaseApi",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "tenant_id",
"value": "={{ 'eq.' + $json.tenant_id }}"
},
{
"name": "type",
"value": "={{ 'eq.overage_warning_' + $json.threshold }}"
},
{
"name": "message",
"value": "={{ 'like.*' + $json.year_month + '*' }}"
},
{
"name": "select",
"value": "id"
}
]
}
},
"id": "wf005-node-check-notified",
"name": "Check Already Notified",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1250,
300
],
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Check if notification was already sent this month for this threshold\nconst notificationResults = $input.all();\nconst tenant = $('Loop Over Tenants').item.json;\n\n// If we got results back from the notifications query, it means already notified\nconst alreadyNotified = notificationResults.length > 0 \n && notificationResults[0].json \n && notificationResults[0].json.id;\n\nreturn [\n {\n json: {\n ...tenant,\n already_notified: !!alreadyNotified\n }\n }\n];"
},
"id": "wf005-node-check-logic",
"name": "Check Notification Status",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1500,
300
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "wf005-cond-not-notified",
"leftValue": "={{ $json.already_notified }}",
"rightValue": false,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
}
},
"id": "wf005-node-if-not-notified",
"name": "Not Already Notified?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1750,
300
]
},
{
"parameters": {
"jsCode": "// Build overage warning email\nconst tenant = $input.all()[0].json;\n\nconst isOver = tenant.usage_percent >= 100;\nconst thresholdLabel = isOver ? 'exceeded' : 'approaching';\nconst urgencyColor = isOver ? '#FF4444' : '#FFB800';\n\nconst subject = isOver\n ? `Action Required: Your TitleFlow AI usage has exceeded your plan limit`\n : `Usage Alert: Your TitleFlow AI usage is at ${tenant.usage_percent}% of your plan limit`;\n\nconst htmlBody = `\n<div style=\"font-family: 'Inter', Arial, sans-serif; max-width: 600px; margin: 0 auto; background: #000000; color: #ffffff; border-radius: 16px; overflow: hidden;\">\n <div style=\"background: linear-gradient(135deg, ${urgencyColor}, #4F1AD6); padding: 32px; text-align: center;\">\n <h1 style=\"margin: 0; font-size: 24px; font-weight: 700;\">TitleFlow AI</h1>\n <p style=\"margin: 8px 0 0; opacity: 0.9; font-size: 14px;\">Usage ${isOver ? 'Overage' : 'Warning'} Alert</p>\n </div>\n <div style=\"padding: 32px;\">\n <p style=\"font-size: 16px; line-height: 1.6;\">Hi <strong>${tenant.company_name}</strong>,</p>\n <p style=\"font-size: 14px; line-height: 1.6; color: #cccccc;\">Your usage is currently <strong style=\"color: ${urgencyColor};\">${thresholdLabel} your ${tenant.plan} plan limit</strong>.</p>\n \n <div style=\"background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); border-radius: 12px; padding: 20px; margin: 20px 0;\">\n <div style=\"display: flex; justify-content: space-between; margin-bottom: 12px;\">\n <span style=\"color: #999999; font-size: 14px;\">Minutes Used</span>\n <span style=\"font-weight: 700; font-size: 14px; color: ${urgencyColor};\">${tenant.total_minutes} / ${tenant.plan_limit}</span>\n </div>\n <div style=\"background: rgba(255,255,255,0.1); border-radius: 999px; height: 8px; overflow: hidden;\">\n <div style=\"background: linear-gradient(90deg, #0080FF, ${urgencyColor}); width: ${Math.min(tenant.usage_percent, 100)}%; height: 100%; border-radius: 999px;\"></div>\n </div>\n <div style=\"text-align: right; margin-top: 8px;\">\n <span style=\"font-size: 12px; color: #666666;\">${tenant.usage_percent}% of plan limit</span>\n </div>\n </div>\n \n ${isOver \n ? '<p style=\"font-size: 14px; color: #FF4444; line-height: 1.6;\"><strong>Overage charges may apply.</strong> Consider upgrading your plan to avoid additional costs.</p>'\n : '<p style=\"font-size: 14px; color: #cccccc; line-height: 1.6;\">You are approaching your monthly limit. Consider upgrading your plan if you expect continued growth.</p>'\n }\n \n <div style=\"text-align: center; margin-top: 24px;\">\n <a href=\"https://www.titleflowai.ai/dashboard/billing\" style=\"display: inline-block; background: linear-gradient(135deg, #0080FF, #4F1AD6); color: #ffffff; text-decoration: none; padding: 12px 32px; border-radius: 999px; font-weight: 600; font-size: 14px;\">Upgrade Plan</a>\n </div>\n </div>\n <div style=\"padding: 16px 32px; text-align: center; font-size: 12px; color: #666666; border-top: 1px solid rgba(255,255,255,0.05);\">\n <p style=\"margin: 0;\">TitleFlow AI AI — Automating title industry communications</p>\n </div>\n</div>`;\n\nreturn [\n {\n json: {\n to: tenant.email,\n subject: subject,\n html: htmlBody,\n tenant_id: tenant.tenant_id,\n company_name: tenant.company_name,\n threshold: tenant.threshold,\n usage_percent: tenant.usage_percent,\n year_month: tenant.year_month\n }\n }\n];"
},
"id": "wf005-node-build-email",
"name": "Build Overage Email",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2000,
300
]
},
{
"parameters": {
"fromEmail": "user@example.com",
"toEmail": "={{ $json.to }}",
"subject": "={{ $json.subject }}",
"emailType": "html",
"html": "={{ $json.html }}"
},
"id": "wf005-node-send-email",
"name": "Send Overage Warning",
"type": "n8n-nodes-base.emailSend",
"typeVersion": 2.1,
"position": [
2250,
300
],
"credentials": {
"smtp": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "create",
"tableId": "notifications",
"dataToSend": "defineBelow",
"fieldsUi": {
"fieldValues": [
{
"fieldName": "tenant_id",
"fieldValue": "={{ $json.tenant_id }}"
},
{
"fieldName": "type",
"fieldValue": "={{ 'overage_warning_' + $json.threshold }}"
},
{
"fieldName": "title",
"fieldValue": "={{ 'Usage at ' + $json.usage_percent + '% of plan limit' }}"
},
{
"fieldName": "message",
"fieldValue": "={{ 'Overage warning (' + $json.threshold + '% threshold) sent to ' + $json.company_name + ' for ' + $json.year_month }}"
},
{
"fieldName": "channel",
"fieldValue": "email"
},
{
"fieldName": "sent_at",
"fieldValue": "={{ new Date().toISOString() }}"
}
]
}
},
"id": "wf005-node-log-notification",
"name": "Log Overage Notification",
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
2500,
300
],
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Daily 9AM ET": {
"main": [
[
{
"node": "Get Tenants Approaching Limit",
"type": "main",
"index": 0
}
]
]
},
"Get Tenants Approaching Limit": {
"main": [
[
{
"node": "Filter & Compute Thresholds",
"type": "main",
"index": 0
}
]
]
},
"Filter & Compute Thresholds": {
"main": [
[
{
"node": "Has Results?",
"type": "main",
"index": 0
}
]
]
},
"Has Results?": {
"main": [
[
{
"node": "Loop Over Tenants",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Tenants": {
"main": [
null,
[
{
"node": "Check Already Notified",
"type": "main",
"index": 0
}
]
]
},
"Check Already Notified": {
"main": [
[
{
"node": "Check Notification Status",
"type": "main",
"index": 0
}
]
]
},
"Check Notification Status": {
"main": [
[
{
"node": "Not Already Notified?",
"type": "main",
"index": 0
}
]
]
},
"Not Already Notified?": {
"main": [
[
{
"node": "Build Overage Email",
"type": "main",
"index": 0
}
],
[
{
"node": "Loop Over Tenants",
"type": "main",
"index": 0
}
]
]
},
"Build Overage Email": {
"main": [
[
{
"node": "Send Overage Warning",
"type": "main",
"index": 0
}
]
]
},
"Send Overage Warning": {
"main": [
[
{
"node": "Log Overage Notification",
"type": "main",
"index": 0
}
]
]
},
"Log Overage Notification": {
"main": [
[
{
"node": "Loop Over Tenants",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"saveManualExecutions": true,
"callerPolicy": "workflowsFromSameOwner",
"timezone": "America/New_York"
},
"meta": {
"templateCredsSetupCompleted": false
},
"active": false,
"tags": [
{
"name": "overage"
},
{
"name": "billing"
},
{
"name": "alerts"
}
]
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
smtpsupabaseApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
WF-005 Overage Warning. Uses httpRequest, emailSend, supabase. Scheduled trigger; 11 nodes.
Source: https://github.com/rafiulislam4246/voice-sdr-title-agent/blob/main/app/n8n-workflows/WF-005-overage-warning.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
WF-008 Monthly Report. Uses supabase, httpRequest, emailSend. Scheduled trigger; 11 nodes.
WF-004 Renewal Reminder. Uses httpRequest, emailSend, supabase. Scheduled trigger; 8 nodes.
Monitors brand mentions across Twitter/X, Reddit, and News APIs in real-time (or scheduled), fetches mentions in parallel, normalizes data, uses AI to analyze sentiment/urgency/topics, detects duplica
Tech Daily Digest. Uses rssFeedRead, emailSend, httpRequest. Scheduled trigger; 22 nodes.
Ai Assistant Agent Full Workflow. Uses supabase, httpRequest, emailSend. Webhook trigger; 18 nodes.