AutomationFlowsAI & RAG › Wf-005 Overage Warning

Wf-005 Overage Warning

WF-005 Overage Warning. Uses httpRequest, emailSend, supabase. Scheduled trigger; 11 nodes.

Cron / scheduled trigger★★★★☆ complexity11 nodesHTTP RequestEmail SendSupabase
AI & RAG Trigger: Cron / scheduled Nodes: 11 Complexity: ★★★★☆ Added:

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 →

Download .json
{
  "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 &mdash; 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.

Pro

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 →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

WF-008 Monthly Report. Uses supabase, httpRequest, emailSend. Scheduled trigger; 11 nodes.

Supabase, HTTP Request, Email Send
AI & RAG

WF-004 Renewal Reminder. Uses httpRequest, emailSend, supabase. Scheduled trigger; 8 nodes.

HTTP Request, Email Send, Supabase
AI & RAG

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

HTTP Request, Email Send
AI & RAG

Tech Daily Digest. Uses rssFeedRead, emailSend, httpRequest. Scheduled trigger; 22 nodes.

RSS Feed Read, Email Send, HTTP Request
AI & RAG

Ai Assistant Agent Full Workflow. Uses supabase, httpRequest, emailSend. Webhook trigger; 18 nodes.

Supabase, HTTP Request, Email Send