{
  "name": "WF-008 Monthly Report",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 9 1 * *"
            }
          ]
        }
      },
      "id": "wf008-node-schedule",
      "name": "1st of Month 9AM ET",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        300
      ]
    },
    {
      "parameters": {
        "operation": "getAll",
        "tableId": "tenants",
        "returnAll": true,
        "filterType": "string",
        "filterString": "subscription_status=eq.active"
      },
      "id": "wf008-node-get-tenants",
      "name": "Get Active Tenants",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        250,
        300
      ],
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "batchSize": 1,
        "options": {}
      },
      "id": "wf008-node-loop",
      "name": "Loop Over Tenants",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        500,
        300
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "https://yepggeqhtspgzlkhqhgo.supabase.co/rest/v1/usage_monthly",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "supabaseApi",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "tenant_id",
              "value": "={{ 'eq.' + $('Loop Over Tenants').item.json.id }}"
            },
            {
              "name": "year_month",
              "value": "={{ (() => { const d = new Date(); d.setMonth(d.getMonth() - 1); return 'eq.' + d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0'); })() }}"
            },
            {
              "name": "select",
              "value": "*"
            }
          ]
        }
      },
      "id": "wf008-node-get-monthly",
      "name": "Get Monthly Usage",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        750,
        300
      ],
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "GET",
        "url": "https://yepggeqhtspgzlkhqhgo.supabase.co/rest/v1/usage_daily",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "supabaseApi",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "tenant_id",
              "value": "={{ 'eq.' + $('Loop Over Tenants').item.json.id }}"
            },
            {
              "name": "date",
              "value": "={{ (() => { const d = new Date(); d.setMonth(d.getMonth() - 1); const start = d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-01'; const end = new Date(d.getFullYear(), d.getMonth() + 1, 0).toISOString().split('T')[0]; return 'gte.' + start + '&date=lte.' + end; })() }}"
            },
            {
              "name": "select",
              "value": "*"
            },
            {
              "name": "order",
              "value": "date.asc"
            }
          ]
        }
      },
      "id": "wf008-node-get-daily",
      "name": "Get Daily Trends",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1000,
        300
      ],
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Compute monthly insights from daily and monthly data\nconst dailyItems = $input.all();\nconst monthlyData = $('Get Monthly Usage').first().json || {};\nconst tenant = $('Loop Over Tenants').item.json;\n\nconst dailyRecords = dailyItems.map(i => i.json).filter(d => d.date);\n\n// Previous month label\nconst prevDate = new Date();\nprevDate.setMonth(prevDate.getMonth() - 1);\nconst monthLabel = prevDate.toLocaleDateString('en-US', { year: 'numeric', month: 'long' });\n\n// Basic stats from monthly summary\nconst totalCalls = monthlyData.total_calls || 0;\nconst totalMinutes = monthlyData.total_minutes || 0;\nconst answered = monthlyData.answered || 0;\nconst missed = monthlyData.missed || 0;\nconst voicemail = monthlyData.voicemail || 0;\nconst escalations = monthlyData.escalations || 0;\nconst daysInMonth = dailyRecords.length || 1;\n\n// Computed insights\nconst avgCallsPerDay = Math.round(totalCalls / daysInMonth * 10) / 10;\nconst answerRate = totalCalls > 0 ? Math.round((answered / totalCalls) * 100) : 0;\nconst missRate = totalCalls > 0 ? Math.round((missed / totalCalls) * 100) : 0;\nconst escalationRate = totalCalls > 0 ? Math.round((escalations / totalCalls) * 100) : 0;\n\n// Busiest day\nlet busiestDay = { date: 'N/A', calls: 0 };\nfor (const day of dailyRecords) {\n  if ((day.total_calls || 0) > busiestDay.calls) {\n    busiestDay = {\n      date: new Date(day.date).toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric' }),\n      calls: day.total_calls\n    };\n  }\n}\n\n// Time & cost savings (1.5x multiplier, $25/hr)\nconst totalHours = Math.round(totalMinutes / 60 * 100) / 100;\nconst hoursSaved = Math.round(totalHours * 1.5 * 100) / 100;\nconst laborCostSaved = Math.round(hoursSaved * 25 * 100) / 100;\n\n// Resolution rate (answered without escalation)\nconst resolvedWithoutEscalation = answered - escalations;\nconst resolutionRate = answered > 0 ? Math.round((resolvedWithoutEscalation / answered) * 100) : 0;\n\n// Build insights array for ai_insights table\nconst insights = [\n  {\n    tenant_id: tenant.id,\n    category: 'trend',\n    severity: 'info',\n    title: 'Monthly Call Volume',\n    message: `${totalCalls} total calls handled in ${monthLabel}, averaging ${avgCallsPerDay} calls per day.`\n  },\n  {\n    tenant_id: tenant.id,\n    category: 'trend',\n    severity: 'info',\n    title: 'Busiest Day',\n    message: `${busiestDay.date} was the busiest day with ${busiestDay.calls} calls.`\n  },\n  {\n    tenant_id: tenant.id,\n    category: 'trend',\n    severity: 'info',\n    title: 'Answer Rate',\n    message: `${answerRate}% answer rate with ${missed} missed calls and ${voicemail} voicemails.`\n  },\n  {\n    tenant_id: tenant.id,\n    category: 'recommendation',\n    severity: 'info',\n    title: 'Time & Cost Savings',\n    message: `TitleFlow AI saved an estimated ${hoursSaved} staff hours ($${laborCostSaved.toLocaleString()} in labor costs) at a 1.5x efficiency multiplier.`\n  },\n  {\n    tenant_id: tenant.id,\n    category: 'trend',\n    severity: 'info',\n    title: 'Resolution Rate',\n    message: `${resolutionRate}% of answered calls resolved without escalation.`\n  }\n];\n\nif (escalationRate > 15) {\n  insights.push({\n    tenant_id: tenant.id,\n    category: 'recommendation',\n    severity: 'info',\n    title: 'High Escalation Rate',\n    message: `Escalation rate was ${escalationRate}%. Consider reviewing AI call scripts to reduce escalations.`\n  });\n}\n\nreturn [\n  {\n    json: {\n      tenant_id: tenant.id,\n      company_name: tenant.company_name || 'Customer',\n      owner_email: tenant.owner_email || tenant.contact_email || '',\n      plan: tenant.plan || 'Standard',\n      month_label: monthLabel,\n      total_calls: totalCalls,\n      total_minutes: totalMinutes,\n      total_hours: totalHours,\n      answered: answered,\n      missed: missed,\n      voicemail: voicemail,\n      escalations: escalations,\n      avg_calls_per_day: avgCallsPerDay,\n      answer_rate: answerRate,\n      miss_rate: missRate,\n      escalation_rate: escalationRate,\n      resolution_rate: resolutionRate,\n      busiest_day: busiestDay.date,\n      busiest_day_calls: busiestDay.calls,\n      hours_saved: hoursSaved,\n      labor_cost_saved: laborCostSaved,\n      insights: insights\n    }\n  }\n];"
      },
      "id": "wf008-node-compute-insights",
      "name": "Compute Insights",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1250,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Insert each insight as a separate item for the Supabase upsert\nconst data = $input.all()[0].json;\nconst insights = data.insights || [];\n\nreturn insights.map(insight => ({\n  json: {\n    tenant_id: insight.tenant_id,\n    category: insight.category,\n    severity: insight.severity,\n    title: insight.title,\n    message: insight.message,\n    month: data.month_label,\n    created_at: new Date().toISOString(),\n    // Pass through report data for downstream nodes\n    _report: {\n      tenant_id: data.tenant_id,\n      company_name: data.company_name,\n      owner_email: data.owner_email,\n      plan: data.plan,\n      month_label: data.month_label,\n      total_calls: data.total_calls,\n      total_minutes: data.total_minutes,\n      total_hours: data.total_hours,\n      answered: data.answered,\n      missed: data.missed,\n      voicemail: data.voicemail,\n      escalations: data.escalations,\n      avg_calls_per_day: data.avg_calls_per_day,\n      answer_rate: data.answer_rate,\n      miss_rate: data.miss_rate,\n      escalation_rate: data.escalation_rate,\n      resolution_rate: data.resolution_rate,\n      busiest_day: data.busiest_day,\n      busiest_day_calls: data.busiest_day_calls,\n      hours_saved: data.hours_saved,\n      labor_cost_saved: data.labor_cost_saved\n    }\n  }\n}));"
      },
      "id": "wf008-node-split-insights",
      "name": "Prepare Insight Records",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1500,
        300
      ]
    },
    {
      "parameters": {
        "operation": "create",
        "tableId": "ai_insights",
        "dataToSend": "defineBelow",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldName": "tenant_id",
              "fieldValue": "={{ $json.tenant_id }}"
            },
            {
              "fieldName": "category",
              "fieldValue": "={{ $json.category }}"
            },
            {
              "fieldName": "severity",
              "fieldValue": "={{ $json.severity }}"
            },
            {
              "fieldName": "title",
              "fieldValue": "={{ $json.title }}"
            },
            {
              "fieldName": "message",
              "fieldValue": "={{ $json.message }}"
            }
          ]
        }
      },
      "id": "wf008-node-insert-insights",
      "name": "Insert AI Insights",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        1750,
        300
      ],
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Build HTML monthly report email from the report data\nconst items = $input.all();\n// Get the report data from the last item (all items carry the same _report)\nconst report = items[items.length - 1].json._report || items[0].json._report || {};\n\nconst r = report;\nconst monthLabel = r.month_label || 'Last Month';\n\nconst subject = `Your TitleFlow AI Monthly Report - ${monthLabel}`;\n\nconst htmlBody = `\n<div style=\"font-family: 'Inter', Arial, sans-serif; max-width: 640px; margin: 0 auto; background: #000000; color: #ffffff; border-radius: 16px; overflow: hidden;\">\n  <!-- Header -->\n  <div style=\"background: linear-gradient(135deg, #0080FF, #4F1AD6); padding: 40px 32px; text-align: center;\">\n    <h1 style=\"margin: 0; font-size: 28px; font-weight: 700;\">TitleFlow AI</h1>\n    <p style=\"margin: 8px 0 0; opacity: 0.9; font-size: 16px;\">Monthly Performance Report</p>\n    <p style=\"margin: 4px 0 0; opacity: 0.7; font-size: 14px;\">${monthLabel}</p>\n  </div>\n  \n  <!-- Summary Stats -->\n  <div style=\"padding: 32px;\">\n    <p style=\"font-size: 16px; line-height: 1.6; margin-bottom: 24px;\">Hi <strong>${r.company_name}</strong>, here is your monthly performance summary:</p>\n    \n    <div style=\"display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 24px;\">\n      <div style=\"flex: 1; min-width: 120px; background: rgba(0, 128, 255, 0.1); border: 1px solid rgba(0, 128, 255, 0.2); border-radius: 12px; padding: 16px; text-align: center;\">\n        <div style=\"font-size: 28px; font-weight: 700; color: #00D9FF;\">${r.total_calls}</div>\n        <div style=\"font-size: 12px; color: #999999; margin-top: 4px;\">Total Calls</div>\n      </div>\n      <div style=\"flex: 1; min-width: 120px; background: rgba(0, 128, 255, 0.1); border: 1px solid rgba(0, 128, 255, 0.2); border-radius: 12px; padding: 16px; text-align: center;\">\n        <div style=\"font-size: 28px; font-weight: 700; color: #00D9FF;\">${r.total_hours}h</div>\n        <div style=\"font-size: 12px; color: #999999; margin-top: 4px;\">Total Hours</div>\n      </div>\n      <div style=\"flex: 1; min-width: 120px; background: rgba(0, 128, 255, 0.1); border: 1px solid rgba(0, 128, 255, 0.2); border-radius: 12px; padding: 16px; text-align: center;\">\n        <div style=\"font-size: 28px; font-weight: 700; color: #00D9FF;\">${r.answer_rate}%</div>\n        <div style=\"font-size: 12px; color: #999999; margin-top: 4px;\">Answer Rate</div>\n      </div>\n    </div>\n    \n    <!-- Detailed Breakdown -->\n    <div style=\"background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08); border-radius: 12px; padding: 20px; margin-bottom: 24px;\">\n      <h3 style=\"margin: 0 0 16px; font-size: 16px; color: #ffffff;\">Call Breakdown</h3>\n      <table style=\"width: 100%; border-collapse: collapse; font-size: 14px;\">\n        <tr style=\"border-bottom: 1px solid rgba(255,255,255,0.06);\">\n          <td style=\"padding: 10px 0; color: #999999;\">Answered</td>\n          <td style=\"padding: 10px 0; text-align: right; font-weight: 600; color: #4ADE80;\">${r.answered}</td>\n        </tr>\n        <tr style=\"border-bottom: 1px solid rgba(255,255,255,0.06);\">\n          <td style=\"padding: 10px 0; color: #999999;\">Missed</td>\n          <td style=\"padding: 10px 0; text-align: right; font-weight: 600; color: #FF6B6B;\">${r.missed}</td>\n        </tr>\n        <tr style=\"border-bottom: 1px solid rgba(255,255,255,0.06);\">\n          <td style=\"padding: 10px 0; color: #999999;\">Voicemail</td>\n          <td style=\"padding: 10px 0; text-align: right; font-weight: 600; color: #FFB800;\">${r.voicemail}</td>\n        </tr>\n        <tr style=\"border-bottom: 1px solid rgba(255,255,255,0.06);\">\n          <td style=\"padding: 10px 0; color: #999999;\">Escalations</td>\n          <td style=\"padding: 10px 0; text-align: right; font-weight: 600; color: #FF6B6B;\">${r.escalations}</td>\n        </tr>\n        <tr style=\"border-bottom: 1px solid rgba(255,255,255,0.06);\">\n          <td style=\"padding: 10px 0; color: #999999;\">Avg Calls/Day</td>\n          <td style=\"padding: 10px 0; text-align: right; font-weight: 600; color: #00D9FF;\">${r.avg_calls_per_day}</td>\n        </tr>\n        <tr>\n          <td style=\"padding: 10px 0; color: #999999;\">Resolution Rate</td>\n          <td style=\"padding: 10px 0; text-align: right; font-weight: 600; color: #4ADE80;\">${r.resolution_rate}%</td>\n        </tr>\n      </table>\n    </div>\n    \n    <!-- Key Insight -->\n    <div style=\"background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08); border-radius: 12px; padding: 20px; margin-bottom: 24px;\">\n      <h3 style=\"margin: 0 0 12px; font-size: 16px; color: #ffffff;\">Key Insights</h3>\n      <p style=\"margin: 0 0 8px; font-size: 14px; color: #cccccc; line-height: 1.6;\"><span style=\"color: #00D9FF;\">&#9679;</span> Busiest day: <strong>${r.busiest_day}</strong> with ${r.busiest_day_calls} calls</p>\n      <p style=\"margin: 0 0 8px; font-size: 14px; color: #cccccc; line-height: 1.6;\"><span style=\"color: #00D9FF;\">&#9679;</span> ${r.resolution_rate}% of calls resolved without human escalation</p>\n      <p style=\"margin: 0; font-size: 14px; color: #cccccc; line-height: 1.6;\"><span style=\"color: #00D9FF;\">&#9679;</span> ${r.escalation_rate}% escalation rate</p>\n    </div>\n    \n    <!-- Savings -->\n    <div style=\"background: linear-gradient(135deg, rgba(0, 128, 255, 0.15), rgba(79, 26, 214, 0.15)); border: 1px solid rgba(0, 217, 255, 0.2); border-radius: 12px; padding: 24px; margin-bottom: 24px; text-align: center;\">\n      <p style=\"margin: 0 0 4px; font-size: 12px; color: #999999; text-transform: uppercase; letter-spacing: 1px;\">Estimated Savings This Month</p>\n      <div style=\"font-size: 36px; font-weight: 700; color: #4ADE80;\">$${r.labor_cost_saved.toLocaleString()}</div>\n      <p style=\"margin: 8px 0 0; font-size: 14px; color: #cccccc;\">${r.hours_saved} staff hours saved at 1.5x efficiency &times; $25/hr</p>\n    </div>\n    \n    <div style=\"text-align: center; margin-top: 24px;\">\n      <a href=\"https://www.titleflowai.ai/dashboard\" style=\"display: inline-block; background: linear-gradient(135deg, #0080FF, #4F1AD6); color: #ffffff; text-decoration: none; padding: 14px 36px; border-radius: 999px; font-weight: 600; font-size: 14px;\">View Full Dashboard</a>\n    </div>\n  </div>\n  \n  <!-- Footer -->\n  <div style=\"padding: 20px 32px; text-align: center; font-size: 12px; color: #666666; border-top: 1px solid rgba(255,255,255,0.05);\">\n    <p style=\"margin: 0 0 4px;\">TitleFlow AI AI &mdash; Automating title industry communications</p>\n    <p style=\"margin: 0;\">You are receiving this because you are subscribed to the ${r.plan} plan.</p>\n  </div>\n</div>`;\n\nreturn [\n  {\n    json: {\n      to: r.owner_email,\n      subject: subject,\n      html: htmlBody,\n      tenant_id: r.tenant_id,\n      company_name: r.company_name,\n      month_label: monthLabel\n    }\n  }\n];"
      },
      "id": "wf008-node-build-email",
      "name": "Build HTML Report 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": "wf008-node-send-email",
      "name": "Send Monthly Report",
      "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": "monthly_report"
            },
            {
              "fieldName": "title",
              "fieldValue": "={{ 'Monthly Report - ' + $json.month_label }}"
            },
            {
              "fieldName": "message",
              "fieldValue": "={{ 'Monthly performance report for ' + $json.month_label + ' sent to ' + $json.company_name }}"
            },
            {
              "fieldName": "channel",
              "fieldValue": "email"
            },
            {
              "fieldName": "sent_at",
              "fieldValue": "={{ new Date().toISOString() }}"
            }
          ]
        }
      },
      "id": "wf008-node-log-notification",
      "name": "Log Report Notification",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        2500,
        300
      ],
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "1st of Month 9AM ET": {
      "main": [
        [
          {
            "node": "Get Active Tenants",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Active Tenants": {
      "main": [
        [
          {
            "node": "Loop Over Tenants",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Tenants": {
      "main": [
        null,
        [
          {
            "node": "Get Monthly Usage",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Monthly Usage": {
      "main": [
        [
          {
            "node": "Get Daily Trends",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Daily Trends": {
      "main": [
        [
          {
            "node": "Compute Insights",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compute Insights": {
      "main": [
        [
          {
            "node": "Prepare Insight Records",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Insight Records": {
      "main": [
        [
          {
            "node": "Insert AI Insights",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert AI Insights": {
      "main": [
        [
          {
            "node": "Build HTML Report Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build HTML Report Email": {
      "main": [
        [
          {
            "node": "Send Monthly Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Monthly Report": {
      "main": [
        [
          {
            "node": "Log Report Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Report 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": "reporting"
    },
    {
      "name": "monthly"
    },
    {
      "name": "insights"
    }
  ]
}