{
  "name": "CFO End — Financial Guardrails",
  "description": null,
  "active": true,
  "isArchived": false,
  "nodes": [
    {
      "parameters": {
        "httpMethod": "GET",
        "path": "cfo-end",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "9d975bdc-bde1-4eed-9b71-e0cf1f7095eb",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        240,
        120
      ],
      "webhookId": "<<REPLACE_ME>>"
    },
    {
      "parameters": {
        "jsCode": "const apiKey = \"<<REPLACE_ME>>\";\nreturn { apiKey };"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        96
      ],
      "id": "api-key",
      "name": "API Key"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://aicivz8c3l.execute-api.ap-south-1.amazonaws.com/dev/mcp/call",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "X-API-Key",
              "value": "={{ $('API Key').item.json.apiKey }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"jsonrpc\": \"2.0\",\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"graphql_query\",\n    \"arguments\": {\n      \"query\": \"query GetGoalItemById($id: ID!) { goalItemById(id: $id) { id body contribution status tags reward progress date period taskRef goalRef subTasks { id body isComplete } } }\",\n      \"variables\": { \"id\": \"{{ $('Webhook').item.json.query.goal_id }}\" }\n    }\n  },\n  \"id\": 1\n}",
        "options": {}
      },
      "id": "377597b1-7a43-4d8f-9d5c-60482c62a54c",
      "name": "Get Goal",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        720,
        120
      ]
    },
    {
      "parameters": {
        "jsCode": "// Unwrap the MCP JSON-RPC envelope into the goal object.\nconst text = $input.first().json.result.content[0].text;\nconst data = JSON.parse(text);\nreturn { json: data.data ? data.data : data };"
      },
      "id": "3421e980-c1f9-4e00-82fa-8c31d45621f0",
      "name": "Parse Goal",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        960,
        120
      ]
    },
    {
      "parameters": {
        "jsCode": "const goal = $('Parse Goal').first().json.goalItemById || {};\nconst subs = Array.isArray(goal.subTasks) ? goal.subTasks : [];\nconst total = subs.length;\nconst done = subs.filter(s => s && s.isComplete === true).length;\nconst pct = total ? Math.round(done * 100 / total) : 0;\nconst completed = subs.filter(s => s && s.isComplete === true).map(s => s.body);\nconst incomplete = subs.filter(s => !(s && s.isComplete === true)).map(s => s.body);\nconst now = new Date();\nconst pad = (x)=>String(x).padStart(2,'0');\nconst today = `${pad(now.getDate())}-${pad(now.getMonth()+1)}-${now.getFullYear()}`;\nconst wk = new Date(now.getTime() - 7*24*3600*1000);\nconst weekStart = `${pad(wk.getDate())}-${pad(wk.getMonth()+1)}-${wk.getFullYear()}`;\nreturn [{ json: { goalItemById: goal, completion: { total, done, pct, completed, incomplete },\n  date: goal.date || today, period: goal.period || 'day', weekStart } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        680,
        96
      ],
      "id": "evaluate",
      "name": "Evaluate"
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "<<REPLACE_ME>>",
          "mode": "list"
        },
        "sheetName": {
          "__rl": true,
          "value": 1131190746,
          "mode": "list",
          "cachedResultName": "CAS"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        900,
        240
      ],
      "id": "read-cas",
      "name": "Read CAS",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "<<REPLACE_ME>>",
          "name": "<<REPLACE_ME>> (was: Google Sheets account)"
        }
      }
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "<<REPLACE_ME>>",
          "mode": "list"
        },
        "sheetName": {
          "__rl": true,
          "value": 1431097187,
          "mode": "list",
          "cachedResultName": "Last3Months"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        900,
        420
      ],
      "id": "read-last3months",
      "name": "Read Last3Months",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "<<REPLACE_ME>>",
          "name": "<<REPLACE_ME>> (was: Google Sheets account)"
        }
      }
    },
    {
      "parameters": {
        "numberInputs": 2
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        1120,
        330
      ],
      "id": "merge-sheets",
      "name": "Merge Sheets"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://aicivz8c3l.execute-api.ap-south-1.amazonaws.com/dev/mcp/call",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "X-API-Key",
              "value": "={{ $('API Key').item.json.apiKey }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\n  \"params\":{\"name\":\"graphql_query\",\"arguments\":{\n    \"query\":\"query($period: String!, $startDate: String!, $endDate: String!) { getProgress(period: $period, startDate: $startDate, endDate: $endDate) { period startDate endDate progressStatement } }\",\n    \"variables\":{ \"period\":\"week\", \"startDate\":\"{{ $('Evaluate').first().json.weekStart }}\", \"endDate\":\"{{ $('Evaluate').first().json.date }}\" }\n  }},\"id\":1\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        900,
        600
      ],
      "id": "get-progress",
      "name": "Get Progress",
      "onError": "continueRegularOutput",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "jsCode": "\nfunction casRecFrom(row){\n  try { return typeof row.record === 'string' ? JSON.parse(row.record) : (row.record || row); }\n  catch(e){ return row || {}; }\n}\nfunction casUnitIndex(rec){\n  // aggregate MF units + nav by ISIN across folios (folios migrate; ISIN is stable)\n  var u = {}, nav = {};\n  var schemes = (rec && rec.mutualFundSchemes) || [];\n  for (var i=0;i<schemes.length;i++){\n    var s = schemes[i], isin = s.isin || '';\n    if (!isin) continue;\n    u[isin] = (u[isin]||0) + (Number(s.units)||0);\n    if (Number(s.nav)) nav[isin] = Number(s.nav);\n  }\n  return { u: u, nav: nav };\n}\nfunction casTotalValue(rec){\n  var ps = (rec && rec.portfolioSummary) || rec || {};\n  return Number(ps.totalPortfolioValue != null ? ps.totalPortfolioValue : (rec||{}).totalPortfolioValue) || 0;\n}\n// Returns INR figures + USD passive for the transition prevRec -> curRec.\nfunction casPassive(prevRec, curRec, usdinr){\n  usdinr = usdinr || 83;\n  var p = casUnitIndex(prevRec), c = casUnitIndex(curRec);\n  var isins = {};\n  Object.keys(p.nav).forEach(function(k){ isins[k]=1; });\n  Object.keys(c.nav).forEach(function(k){ isins[k]=1; });\n  var netContribINR = 0;\n  Object.keys(isins).forEach(function(k){\n    var du = (c.u[k]||0) - (p.u[k]||0);\n    var navNow = c.nav[k] || p.nav[k] || 0;\n    netContribINR += du * navNow;\n  });\n  var dTotalINR = casTotalValue(curRec) - casTotalValue(prevRec);\n  var apprecINR = dTotalINR - netContribINR;              // = Delta-gain (market appreciation only)\n  var passiveUSD = Math.round(Math.max(0, apprecINR / usdinr) * 100) / 100;\n  return {\n    deltaTotalINR: Math.round(dTotalINR),\n    netContribINR: Math.round(netContribINR),\n    apprecINR: Math.round(apprecINR),\n    passiveUSD: passiveUSD\n  };\n}\n\nfunction computeFinancials(casRows, tx, trainingRxSrc, targets, usdinr){\n  usdinr = usdinr || 83;\n  const TRAINING_RX = new RegExp(trainingRxSrc, 'i');\n\n  // passive series from CAS transitions (Delta total value - net SIP contributions)\n  const casSorted = (casRows||[]).slice().filter(r=>r&&r.monthKey).sort((a,b)=> a.monthKey<b.monthKey?-1:1);\n  const passiveByKey = {}; const passiveSeries = [];\n  for (let i=1;i<casSorted.length;i++){\n    const r = casPassive(casRecFrom(casSorted[i-1]), casRecFrom(casSorted[i]), usdinr);\n    passiveByKey[casSorted[i].monthKey] = r.passiveUSD;\n    passiveSeries.push(Object.assign({ monthKey: casSorted[i].monthKey }, r));\n  }\n  let passiveMonthlyUSD = 0, passiveNote = 'no CAS/portfolio data available';\n  if (passiveSeries.length){\n    const last = passiveSeries[passiveSeries.length-1];\n    passiveMonthlyUSD = last.passiveUSD;\n    passiveNote = 'CAS ' + last.monthKey + ': dTotal INR ' + last.deltaTotalINR + ' - contrib INR ' +\n      last.netContribINR + ' = passive INR ' + last.apprecINR + ' = USD ' + last.passiveUSD.toFixed(2);\n  } else if (casSorted.length === 1){ passiveNote = 'only one CAS month; passive growth unknown'; }\n\n  const rowsAll = (tx||[]).map(t => {\n    const m = String(t.Date).match(/(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})/);\n    return { d: m ? { y:+m[3], mo:+m[1], day:+m[2], key: m[3]+'-'+String(m[1]).padStart(2,'0') } : null,\n      desc: String(t['Full Description'] || t.Description || ''),\n      hint: String(t['Category Hint'] || ''), amount: Number(t.Amount) || 0 };\n  }).filter(r => r.d);\n\n  const daysInMonth = (y,mo) => new Date(y, mo, 0).getDate();\n  let minV=null, maxV=null, minD=null, maxD=null;\n  for (const r of rowsAll){ const v = r.d.y*10000 + r.d.mo*100 + r.d.day;\n    if (minV==null||v<minV){minV=v;minD=r.d;} if (maxV==null||v>maxV){maxV=v;maxD=r.d;} }\n  const completeKeys = new Set();\n  if (minD && maxD){\n    let cy=minD.y, cmo=minD.mo;\n    while (cy<maxD.y || (cy===maxD.y && cmo<=maxD.mo)){\n      const last = daysInMonth(cy,cmo);\n      const startCovered = (minD.y<cy)||(minD.y===cy&&minD.mo<cmo)||(minD.y===cy&&minD.mo===cmo&&minD.day<=1);\n      const endCovered   = (maxD.y>cy)||(maxD.y===cy&&maxD.mo>cmo)||(maxD.y===cy&&maxD.mo===cmo&&maxD.day>=last);\n      if (startCovered && endCovered) completeKeys.add(cy+'-'+String(cmo).padStart(2,'0'));\n      cmo++; if (cmo>12){cmo=1;cy++;}\n    }\n  }\n  const distinctMonths = new Set(rowsAll.map(r=>r.d.key)).size;\n  const useComplete = completeKeys.size > 0;\n  const rows = useComplete ? rowsAll.filter(r => completeKeys.has(r.d.key)) : rowsAll;\n  const divisor = Math.max(1, useComplete ? completeKeys.size : distinctMonths);\n  const basisNote = useComplete\n    ? (completeKeys.size + ' complete month(s): ' + [...completeKeys].sort().join(', '))\n    : (distinctMonths + ' month(s) of partial data (no full month available)');\n\n  const RX_PAYROLL = /payroll|direct dep|\\bsalary\\b|paycheck|dir dep/i;\n  const RX_SAVINGS = /transfer to\\b[^]*\\bsav|to sav\\b|vanguard|fidelity|robinhood|schwab|\\broth\\b|401k|\\bira\\b|acorns|betterment|brokerage|wealthfront/i;\n  const RX_DEBT    = /card payment|card online payment|citi card|credit card|autopay|amex e?payment|chase credit|discover.*payment|mortgage|\\brent\\b|auto ?loan|car ?loan|student loan|\\bloan\\b|\\bemi\\b/i;\n  const RX_INTERNAL= /online transfer to chk|online transfer from|transfer from sav|transfer from chk|real ?time transfer|zelle payment to /i;\n  const titleize = s => s.toLowerCase().replace(/_/g,' ').replace(/\\b\\w/g, c=>c.toUpperCase());\n  function classify(r){\n    const a = r.amount, hint = r.hint.toUpperCase();\n    const primary = hint.split(':')[0].trim(), detailed = (hint.split(':')[1]||'').trim();\n    if (primary){\n      if (primary === 'INCOME'){ if (a>0 && !/INTEREST|DIVIDEND/.test(detailed)) return { t: TRAINING_RX.test(r.desc)?'training':'payroll', v:a }; return { t:'skip' }; }\n      if (primary === 'TRANSFER_IN') return { t:'skip' };\n      const abs = a < 0 ? -a : 0;\n      if (primary === 'LOAN_PAYMENTS') return { t:'liabilities', v:abs };\n      if (detailed === 'RENT_AND_UTILITIES_RENT') return { t:'liabilities', v:abs };\n      if (/SAVINGS|INVESTMENT_AND_RETIREMENT/.test(detailed)) return { t:'savings', v:abs };\n      if (primary === 'TRANSFER_OUT') return { t:'skip' };\n      if (a < 0) return { t:'expenses', v:abs, cat: titleize(primary)||'Other' };\n      return { t:'skip' };\n    }\n    if (a > 0){ if (RX_PAYROLL.test(r.desc)) return { t: TRAINING_RX.test(r.desc)?'training':'payroll', v:a }; return { t:'skip' }; }\n    const abs = -a;\n    if (RX_SAVINGS.test(r.desc)) return { t:'savings', v:abs };\n    if (RX_DEBT.test(r.desc)) return { t:'liabilities', v:abs };\n    if (RX_INTERNAL.test(r.desc)) return { t:'skip' };\n    return { t:'expenses', v:abs, cat:'Other' };\n  }\n\n  const monthAgg = {};\n  const bucket = k => monthAgg[k] || (monthAgg[k] = { payroll:0, training:0, expenses:0, liabilities:0, savings:0 });\n  for (const r of rowsAll){ const c = classify(r); if (c.t==='skip') continue; bucket(r.d.key)[c.t] += c.v; }\n\n  let lastPassive = 0;\n  const monthLabelOf = k => { const p=k.split('-'); return new Date(+p[0],+p[1]-1,1).toLocaleString('en-US',{month:'short',year:'2-digit'}); };\n  const byMonth = Object.keys(monthAgg).sort().map(k => {\n    const b = monthAgg[k]; const active = b.payroll + b.training;\n    if (passiveByKey[k] != null) lastPassive = passiveByKey[k];\n    const passive = lastPassive; const income = active + passive;\n    const savingsRate = income > 0 ? Math.round(b.savings / income * 100) : 0;\n    return { monthKey:k, monthLabel: monthLabelOf(k),\n      payroll:Math.round(b.payroll), training:Math.round(b.training), active:Math.round(active),\n      passive:Math.round(passive), expenses:Math.round(b.expenses), liabilities:Math.round(b.liabilities),\n      savings:Math.round(b.savings), savingsRate,\n      trainingTarget: targets.trainingMonthlyUSD, savingsTarget: Math.round(income * targets.savingsRatePct / 100),\n      passiveTarget: Math.round(b.expenses) };  // security stage = passive income covers essential expenses\n  });\n  const trajComplete = byMonth.filter(m => completeKeys.has(m.monthKey));\n  const trajectory = (trajComplete.length ? trajComplete : byMonth).slice(-3);\n\n  let payrollSum=0, trainingSum=0, expenseSum=0, liabilitySum=0, savingsSum=0; const catMap={};\n  for (const r of rows){ const c = classify(r);\n    if (c.t==='payroll') payrollSum += c.v; else if (c.t==='training') trainingSum += c.v;\n    else if (c.t==='liabilities') liabilitySum += c.v; else if (c.t==='savings') savingsSum += c.v;\n    else if (c.t==='expenses'){ expenseSum += c.v; const k=c.cat||'Other'; catMap[k]=(catMap[k]||0)+c.v; } }\n  const per = x => Math.round(x / divisor);\n  const computed = {\n    payroll: per(payrollSum), training: per(trainingSum), activeIncome: per(payrollSum+trainingSum),\n    expenses: per(expenseSum), liabilities: per(liabilitySum), savings: per(savingsSum),\n    passiveIncome: Math.round(passiveMonthlyUSD),\n    categories: Object.keys(catMap).map(k => ({ name:k, amountUSD: per(catMap[k]) })).sort((a,b)=>b.amountUSD-a.amountUSD).slice(0,6),\n    basisNote, hasTx: rows.length > 0 };\n  return { byMonth, trajectory, computed, passiveSeries, passiveByKey, passiveMonthlyUSD, passiveNote, basisNote };\n}\n\nconst ev = $('Evaluate').first().json;\nconst goal = ev.goalItemById || {};\nconst c = ev.completion || {};\nlet cas = [], tx = [];\ntry { cas = $('Read CAS').all().map(i=>i.json); } catch(e) {}\ntry { tx = $('Read Last3Months').all().map(i=>i.json); } catch(e) {}\nconst F = computeFinancials(cas, tx, \"training|course|coaching|workshop|cohort|mentor|tutor|webinar|masterclass|topmate|gumroad|teachable|udemy|thinkific|kajabi|podia|stripe|razorpay|payoneer|paypal|wise \", {\"trainingMonthlyUSD\": 500, \"savingsRatePct\": 25}, 83);\nconst comp = F.computed;\nconst totalIncome = comp.activeIncome + comp.passiveIncome;\nconst trend = F.trajectory.map(m => m.monthLabel + ' sav$' + m.savings + '/pass$' + m.passive + '/train$' + m.training).join(', ');\nconst systemPrompt = \"## Overview\\n\\nYou are an expert financial coach and daily task facilitator designed to guide users through the Five Levels of Financial Freedom Framework. Your role is to create personalized, actionable daily tasks that progressively move users toward financial independence and absolute financial freedom.\\n\\n## The Five Levels of Financial Freedom Framework\\n\\n### Core Metrics\\n- **L** = Liability (% of income/assets)\\n- **E** = Expense (% of income)\\n- **PI** = Passive Income (% of income)\\n- **S** = Savings (% of income)\\n- **AI** = Active Income (primary income source)\\n- **A** = Assets (net worth)\\n\\n### Stage 0: Financial Resolution\\n**Target Metrics:** 70% L, 30% E, 0% S\\n**Objective:** Clear high-priority debt to reduce liability to 40% or increase income to reduce debt ratio\\n\\n**Key Actions:**\\n- Start Emergency Fund (3-6 months of expenses)\\n- Implement Snowball or Avalanche debt repayment technique\\n- Motivate user around \\\"paying yourself first\\\" concept\\n- Focus on psychological wins with quick debt payoffs\\n\\n**Recommended Resources:**\\n- *Total Money Makeover* - Dave Ramsey\\n- *Think and Grow Rich* - Napoleon Hill\\n- Snowball/Avalanche debt methodology\\n\\n---\\n\\n### Stage 1: Financial Security\\n**Target Metrics:** 40% L, 50% E, 10% S\\n**Condition:** Liabilities still exceed assets (L > A), but expenses equal active income (E = AI)\\n\\n**Key Actions:**\\n- Save 10% of active income consistently (\\\"pay yourself first\\\")\\n- Explain the \\\"soul purchasing\\\" concept (why saving is about personal freedom)\\n- Begin passive income planning\\n- Build emergency fund to 6 months of expenses\\n- Develop a comprehensive financial plan\\n\\n**Daily Focus Areas:**\\n- Track spending and optimize expenses\\n- Allocate 10% of income to savings automatically\\n- Research passive income opportunities aligned with skills\\n- Build financial literacy through reading and learning\\n\\n**Recommended Resources:**\\n- *The Richest Man in Babylon* - George S. Clason\\n- *The 10X Rule* - Grant Cardone\\n- Personal finance fundamentals courses\\n\\n---\\n\\n### Stage 2: Financial Vitality\\n**Target Metrics:** 33% L, 40% E (\\u00b17%), 20% S\\n**Condition:** Liabilities still exceed assets (L > A), but expenses now less than active income (E < AI), passive income begins\\n\\n**Key Actions:**\\n- Build synergistic compound asset income streams\\n- Understand that money can be created through leverage and systems\\n- Develop multiple passive income sources (investments, royalties, businesses)\\n- Optimize debt repayment through RDPD (Revised Debt Payment Discipline)\\n- Increase savings rate to 20%\\n- Begin outsourcing non-core activities\\n\\n**Daily Focus Areas:**\\n- Invest surplus income (20% savings rate)\\n- Build first passive income stream\\n- Master one investment vehicle (stocks, real estate, digital products, etc.)\\n- Develop business/side income concepts\\n- Study wealth multiplication strategies\\n\\n**Recommended Resources:**\\n- *Rich Dad Poor Dad* - Robert Kiyosaki\\n- *The Four-Hour Work Week* - Tim Ferriss\\n- *Cashflow Quadrant* - Robert Kiyosaki\\n- *MONEY Master the Game* - Tony Robbins\\n- Passive income course specializing in your interest area\\n\\n---\\n\\n### Stage 3: Financial Independence\\n**Target Metrics:** 33% L, 33% E (\\u00b14%), 30% S\\n**Condition:** Assets now exceed liabilities (L < A), passive income covers expenses (E = PI), passive income is stable\\n\\n**Key Actions:**\\n- Transition mindset from wealth accumulation to wealth optimization\\n- Ensure passive income is sustainable and stable\\n- Increase savings rate to 30% for accelerated asset growth\\n- Build diversified asset portfolio\\n- Plan for lifestyle optimization (not limitation)\\n\\n**Daily Focus Areas:**\\n- Monitor and optimize existing passive income streams\\n- Build new complementary income sources\\n- Invest aggressively in assets (30% savings rate)\\n- Study market trends and investment strategies\\n- Plan transition strategies for career flexibility\\n\\n**Recommended Resources:**\\n- *The Unshakeable* - Tony Robbins & Peter Mallouk\\n- *The Millionaire Fastlane* - MJ DeMarco\\n- Advanced investment and portfolio management courses\\n\\n---\\n\\n### Stage 4: Financial Freedom\\n**Target Metrics:** 20% L, 20% E (\\u00b15%), 55% S\\n**Condition:** Passive income exceeds expenses (E < PI), passive income supports investment growth\\n\\n**Key Actions:**\\n- Passive income now covers all lifestyle expenses\\n- Reinvest 55% of total income into assets\\n- Work becomes optional (continue for growth/purpose, not survival)\\n- Leverage passive income to accelerate wealth building\\n- Design ideal lifestyle and optimize for fulfillment\\n\\n**Daily Focus Areas:**\\n- Optimize investment portfolio returns\\n- Build or expand business ventures (discretionary)\\n- Mentor others (compound your impact)\\n- Plan philanthropic and legacy goals\\n- Develop business/creative projects aligned with passion\\n\\n**Recommended Resources:**\\n- Lifestyle design books and courses\\n- Advanced business strategy and scaling\\n- Wealth preservation and tax optimization resources\\n\\n---\\n\\n### Stage 5: Absolute Financial Freedom\\n**Target Metrics:** 0% L, 5% E, 95% S\\n**Condition:** No debt, minimal expenses relative to assets, maximum autonomy and choice\\n\\n**Key Actions:**\\n- Achieve complete financial autonomy\\n- Work becomes purely optional and passion-driven\\n- Design life around purpose and impact (not financial necessity)\\n- Focus on legacy and systemic change\\n- Explore new frontiers (as framework creator noted: \\\"rocketship for spreading new life forms\\\")\\n\\n**Daily Focus Areas:**\\n- Execute on life's mission and purpose\\n- Build transformative projects with no ROI requirement\\n- Mentor and guide others toward their freedom\\n- Create lasting impact and legacy\\n- Continuous learning and exploration\\n\\n**Recommended Resources:**\\n- Philosophy and meaning-making literature\\n- Advanced entrepreneurship and impact investing\\n- Leadership and organizational design courses\\n\\n---\\n\\n## AI Task Generation Framework\\n\\n### When generating daily tasks, use this hierarchy:\\n\\n1. **Assess Current Stage**\\n   - Ask user their current liability %, expense %, and savings %\\n   - Identify exact stage: Resolution \\u2192 Security \\u2192 Vitality \\u2192 Independence \\u2192 Freedom \\u2192 Absolute\\n\\n2. **Generate Stage-Appropriate Tasks**\\n   - Focus on 3-5 daily actionable tasks\\n   - Mix tactical (immediate actions) with strategic (long-term positioning)\\n   - Ensure tasks compound over time\\n\\n3. **Task Categories by Stage**\\n\\n   **Financial Resolution & Security:**\\n   - Expense tracking and optimization\\n   - Debt payoff progress (snowball/avalanche)\\n   - Emergency fund building\\n   - Financial literacy (reading/courses)\\n   - Income optimization\\n\\n   **Vitality & Independence:**\\n   - Passive income building/optimization\\n   - Investment research and allocation\\n   - Side income development\\n   - Asset portfolio monitoring\\n   - Leverage and scaling strategies\\n\\n   **Freedom & Absolute:**\\n   - Portfolio optimization and rebalancing\\n   - Business and venture development\\n   - Mentor and guide activities\\n   - Impact and legacy planning\\n   - Lifestyle design and execution\\n\\n4. **Task Customization**\\n   - Incorporate user's profession and skills\\n   - Consider their available time commitment\\n   - Respect their risk tolerance and values\\n   - Account for existing income sources\\n   - Build on past successes and learnings\\n\\n5. **Motivation and Accountability**\\n   - Connect tasks to larger vision (why matters)\\n   - Celebrate milestone achievements\\n   - Use psychological principles (momentum, commitment, identity)\\n   - Encourage peer accountability and community\\n   - Adjust difficulty as user progresses\\n\\n---\\n\\n## Daily Task Template\\n\\nFor each task, provide:\\n\\n```\\n**Task:** [Clear, actionable task]\\n**Stage:** [Which stage this task belongs to]\\n**Time Required:** [15 min / 30 min / 1 hour / ongoing]\\n**Category:** [Expense Management / Debt Payoff / Income Building / Investing / Learning]\\n**Action Steps:**\\n1. [Specific action]\\n2. [Specific action]\\n3. [Specific action]\\n\\n**Why This Matters:** [Connect to stage progression and financial freedom goal]\\n**Success Indicator:** [How to measure completion/success]\\n**Resources:** [Specific tools, articles, courses, or templates needed]\\n```\\n\\n---\\n\\n## Conversation Approach\\n\\n### Initial Conversation\\n1. Welcome user and explain the framework\\n2. Ask diagnostic questions to identify their current stage\\n3. Share what stage represents and what they're working toward\\n4. Celebrate progress to date\\n5. Set 30-day and 90-day milestones\\n\\n### Regular Interaction\\n1. Check in on previous day's tasks and learnings\\n2. Address blockers or challenges\\n3. Adjust tasks based on feedback and circumstances\\n4. Introduce new concepts aligned with next stage\\n5. Share stories and examples of others at similar stages\\n\\n### When User Struggles\\n1. Validate the difficulty\\n2. Break tasks into even smaller micro-habits\\n3. Reconnect to emotional \\\"why\\\"\\n4. Offer alternative approaches\\n5. Provide extra resources or accountability structures\\n\\n### Stage Progression\\n1. Acknowledge when user data indicates stage advancement\\n2. Celebrate the milestone\\n3. Explain next stage in detail\\n4. Introduce new task categories\\n5. Share new recommended resources\\n\\n---\\n\\n## Key Philosophical Principles\\n\\n1. **Progress Over Perfection** - Consistency beats optimization; imperfect action beats perfect planning\\n\\n2. **Compound Effect** - Small daily actions compound over months and years into transformation\\n\\n3. **Mindset Evolution** - Financial freedom is as much psychological as mathematical\\n\\n4. **Systems Not Goals** - Build systems and habits that naturally produce financial results\\n\\n5. **Purpose Integration** - Tie financial decisions to personal values and life purpose\\n\\n6. **Automation and Leverage** - Use technology and systems to reduce friction and increase consistency\\n\\n7. **Continuous Learning** - Financial mastery requires ongoing education and skill development\\n\\n8. **Community and Accountability** - Share journey with others, mentor and be mentored\\n\\n9. **Celebrate Wins** - Acknowledge progress to maintain motivation and momentum\\n\\n10. **Adaptability** - Adjust strategies based on life changes, market conditions, and new opportunities\\n\\n---\\n\\n## Example Daily Tasks by Stage\\n\\n### Financial Security Stage - Day 1\\n- **Task 1:** Log all expenses from yesterday (5 min) - Build awareness\\n- **Task 2:** Calculate your current L%, E%, S% based on last month's data (15 min) - Know your baseline\\n- **Task 3:** Set up automatic 10% transfer to savings account (10 min) - Start \\\"paying yourself first\\\"\\n- **Task 4:** Read Chapter 1 of *The Richest Man in Babylon* (20 min) - Build passive income mindset\\n- **Task 5:** Identify one expense you could cut this month (10 min) - Increase savings rate\\n\\n### Vitality Stage - Day 1\\n- **Task 1:** Research 3 passive income opportunities in your field (30 min) - Explore options\\n- **Task 2:** Model out compound returns on your current 20% savings rate over 10 years (20 min) - Visualize potential\\n- **Task 3:** Set up investment account and research your first index fund (30 min) - Take action\\n- **Task 4:** Listen to podcast on real estate or stock investing (30 min) - Build knowledge\\n- **Task 5:** Calculate your \\\"financial vitality number\\\" (monthly expenses for E=PI goal) (15 min) - Set target\\n\\n### Independence Stage - Day 1\\n- **Task 1:** Review passive income streams and measure YTD returns (30 min) - Monitor performance\\n- **Task 2:** Model next investment opportunity through 30-year projection (20 min) - Think long-term\\n- **Task 3:** Work on second passive income stream development for 1 hour (60 min) - Diversify\\n- **Task 4:** Update net worth and celebrate progress (15 min) - Acknowledge wins\\n- **Task 5:** Plan one business idea or side project for exploration (30 min) - Prepare for freedom stage\\n\\n---\\n\\n## Success Metrics to Track\\n\\n- **Monthly:** Liability %, Expense %, Savings %, Net Worth, Passive Income Generated\\n- **Quarterly:** Stage progression, New income streams created, Habits established\\n- **Annually:** Total wealth accumulated, Financial independence score, Impact created\\n\\n---\\n\\n## Creator's Note\\n\\nThis framework reflects a philosophy that financial freedom is not just about numbers, but about reclaiming autonomy, creating options, and eventually having the capacity to spread positive life in the universe. Progress through these stages is progress toward personal liberation and the ability to contribute meaningfully to the world.\\n\\nYour role is to be a consistent, empathetic guide helping users progress through this journey one daily task at a time.\\n\\n\\n\\n---\\n\\n## OUTPUT CONTRACT (STRICT - END OF ROUTINE)\\n\\nReturn ONLY a single minified JSON object and NOTHING else. It MUST match this shape:\\n{\\\"stage\\\":\\\"resolution|security|vitality|independence|freedom|absolute\\\",\\\"stageLabel\\\":\\\"short human label\\\",\\\"metrics\\\":{\\\"income\\\":0,\\\"expense\\\":0,\\\"liabilities\\\":0,\\\"passive\\\":0,\\\"savings\\\":0},\\\"completedSummary\\\":\\\"warm, specific summary of what they did today\\\",\\\"advanced\\\":false,\\\"nextStep\\\":\\\"one concrete action for tomorrow\\\",\\\"highlights\\\":[\\\"savings\\\"]}\\nRules: metrics are integer % of total monthly income. Use the DETERMINISTIC METRICS provided; do not recompute them. \\\"stage\\\" MUST be one of the six lowercase keys. \\\"advanced\\\" is true only if they clearly progressed a stage. \\\"highlights\\\" lists fw-logo focus keys.\";\nconst userMessage =\n  'A user is closing out their daily CFO routine. Their goal today was:\\n\"\"\"\\n' + (goal.body||'') + '\\n\"\"\"\\n\\n' +\n  'They were assigned ' + (c.total||0) + ' action(s) and completed ' + (c.done||0) + ' (' + (c.pct||0) + '%).\\n' +\n  'Completed: ' + JSON.stringify(c.completed||[]) + '\\nNot done: ' + JSON.stringify(c.incomplete||[]) + '\\n\\n' +\n  'DETERMINISTIC MONTHLY METRICS (use as-is, do NOT recompute):\\n' +\n  '- Active income $' + comp.activeIncome + ' (payroll $' + comp.payroll + ' + training $' + comp.training + ')\\n' +\n  '- Passive income $' + comp.passiveIncome + ' (portfolio appreciation net of new SIP; ' + F.passiveNote + ')\\n' +\n  '- Expenses $' + comp.expenses + ', Liabilities $' + comp.liabilities + ', Savings $' + comp.savings + '\\n' +\n  '- Trajectory: ' + (trend || 'not enough complete months yet') + '\\n\\n' +\n  'Reassess their Five-Levels stage, reflect warmly on what they accomplished today, and give ONE concrete next step for tomorrow. ' +\n  'Return ONLY the JSON object from the output contract.';\nreturn [{ json: { systemPrompt, userMessage, _trajectory: F.trajectory, _computed: comp,\n  _totalIncome: totalIncome, _passiveNote: F.passiveNote } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1340,
        330
      ],
      "id": "build-context",
      "name": "Build Context"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://openrouter.ai/api/v1/chat/completions",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "openRouterApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "HTTP-Referer",
              "value": "https://workflows.grvpanchal.me"
            },
            {
              "name": "X-Title",
              "value": "CFO Financial Coach"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ models: ['~google/gemini-flash-latest'], messages: [ { role: 'system', content: $json.systemPrompt }, { role: 'user', content: $json.userMessage } ], temperature: 0.5, max_tokens: 2000, top_p: 0.9, reasoning: { enabled: false }, response_format: { type: 'json_object' } }) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1560,
        330
      ],
      "id": "cfo-agent",
      "name": "CFO Agent",
      "credentials": {
        "openRouterApi": {
          "id": "<<REPLACE_ME>>",
          "name": "<<REPLACE_ME>> (was: OpenRouter account)"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const resp = $('CFO Agent').first().json;\nconst raw = (resp && resp.choices && resp.choices[0] && resp.choices[0].message && resp.choices[0].message.content) || '';\nfunction extractJson(text){\n  if (text && typeof text === 'object') return text;\n  let s = String(text).replace(/```json/gi,'```').replace(/```/g,'').trim();\n  try { return JSON.parse(s); } catch(e){}\n  let best=null;\n  for (let i=0;i<s.length;i++){ if (s[i] !== '{') continue; let depth=0;\n    for (let j=i;j<s.length;j++){ if (s[j]==='{') depth++; else if (s[j]==='}'){ depth--; if(depth===0){\n      try { const o=JSON.parse(s.slice(i,j+1)); if(o && (o.metrics||o.completedSummary||o.stage||o.nextStep)) best=o; } catch(e){} break; }}}}\n  return best;\n}\nlet data = extractJson(raw);\nif (!data) data = { stage:'security', stageLabel:'Keep going', metrics:{income:100,expense:50,liabilities:40,passive:0,savings:10}, completedSummary:String(raw).slice(0,400)||'Reflection unavailable.', advanced:false, nextStep:'Review your spending and pick one improvement.', highlights:['savings'] };\ndata.highlights = Array.isArray(data.highlights)?data.highlights:['savings'];\n\n// override metrics with DETERMINISTIC figures (% of total income) so the reflection is grounded\nconst bc = $('Build Context').first().json;\nconst comp = bc._computed || {}; const ti = bc._totalIncome || 0;\nconst pctI = x => ti > 0 ? Math.round((Number(x)||0) / ti * 100) : 0;\ndata.metrics = Object.assign({}, data.metrics, {\n  income: 100, active: pctI(comp.activeIncome), payroll: pctI(comp.payroll), training: pctI(comp.training),\n  passive: pctI(comp.passiveIncome), expense: pctI(comp.expenses),\n  liabilities: pctI(comp.liabilities), savings: pctI(comp.savings) });\n\nconst ev = $('Evaluate').first().json;\nlet progressStatement = '';\ntry {\n  const pr = $('Get Progress').first().json;\n  const t = pr && pr.result ? JSON.parse(pr.result.content[0].text) : null;\n  const ps = t && (t.data ? t.data.getProgress : t.getProgress);\n  if (ps && ps.progressStatement) progressStatement = ps.progressStatement;\n} catch(e) {}\nreturn [{ json: { ...data, _completion: ev.completion, _date: ev.date, _period: ev.period,\n  _progressStatement: progressStatement, _trajectory: bc._trajectory || [],\n  _usd: { active: comp.activeIncome, payroll: comp.payroll, training: comp.training,\n          passive: comp.passiveIncome, savings: comp.savings } } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1780,
        330
      ],
      "id": "parse-agent",
      "name": "Parse Agent"
    },
    {
      "parameters": {
        "jsCode": "\nfunction trajectorySVG(tj){\n  const esc = (s)=>String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');\n  const usd = (x)=>'$'+Math.round(Number(x)||0).toLocaleString('en-US');\n  const W=720,H=340,PL=54,PR=18,PT=24,PB=46;\n  const series = [ {k:'savings',name:'Savings',color:'#34d399'},\n    {k:'training',name:'Training',color:'#f59e0b'}, {k:'passive',name:'Passive',color:'#a78bfa'} ];\n  const n = tj.length; let maxV = 1;\n  for (const p of tj){ for (const s of series) maxV=Math.max(maxV, Number(p[s.k])||0);\n    maxV=Math.max(maxV, Number(p.trainingTarget)||0, Number(p.savingsTarget)||0, Number(p.passiveTarget)||0); }\n  maxV = maxV*1.15;\n  const xAt = i => PL + (n<=1 ? (W-PL-PR)/2 : (i*(W-PL-PR)/(n-1)));\n  const yAt = v => PT + (H-PT-PB) * (1 - (Number(v)||0)/maxV);\n  const path = key => tj.map((p,i)=>(i?'L':'M')+xAt(i).toFixed(1)+','+yAt(p[key]).toFixed(1)).join(' ');\n  const dots = (key,color)=> tj.map((p,i)=>'<circle cx=\"'+xAt(i).toFixed(1)+'\" cy=\"'+yAt(p[key]).toFixed(1)+'\" r=\"3.5\" fill=\"'+color+'\"/>').join('');\n  let grid=''; const GN=4;\n  for (let g=0;g<=GN;g++){ const v=maxV*g/GN, y=yAt(v);\n    grid += '<line x1=\"'+PL+'\" y1=\"'+y.toFixed(1)+'\" x2=\"'+(W-PR)+'\" y2=\"'+y.toFixed(1)+'\" stroke=\"#334155\" stroke-width=\"1\"/>'+\n            '<text x=\"'+(PL-8)+'\" y=\"'+(y+4).toFixed(1)+'\" fill=\"#64748b\" font-size=\"11\" text-anchor=\"end\">'+usd(v)+'</text>'; }\n  const xlabels = tj.map((p,i)=>'<text x=\"'+xAt(i).toFixed(1)+'\" y=\"'+(H-PB+20)+'\" fill=\"#94a3b8\" font-size=\"12\" text-anchor=\"middle\">'+esc(p.monthLabel)+'</text>').join('');\n  const lines = series.map(s=>'<path d=\"'+path(s.k)+'\" fill=\"none\" stroke=\"'+s.color+'\" stroke-width=\"2.5\"/>'+dots(s.k,s.color)).join('');\n  const tgt = (key,color)=> tj.length? '<path d=\"'+path(key)+'\" fill=\"none\" stroke=\"'+color+'\" stroke-width=\"1.6\" stroke-dasharray=\"5 4\" opacity=\"0.8\"/>':'';\n  // dashed targets in matching colours: savings, training, and passive (passive target = essentials = security stage)\n  const targets = tgt('savingsTarget','#34d399') + tgt('trainingTarget','#f59e0b') + tgt('passiveTarget','#a78bfa');\n  const legend = series.map((s,i)=>'<g transform=\"translate('+(PL+i*128)+','+(PT-6)+')\"><rect width=\"12\" height=\"12\" rx=\"2\" fill=\"'+s.color+'\"/><text x=\"18\" y=\"11\" fill=\"#cbd5e1\" font-size=\"12\">'+s.name+'</text></g>').join('');\n  return '<svg viewBox=\"0 0 '+W+' '+H+'\" width=\"100%\" preserveAspectRatio=\"xMidYMid meet\" role=\"img\" aria-label=\"Savings and earnings trajectory\">'+grid+targets+lines+xlabels+'<g>'+legend+'</g><text x=\"'+(W-PR)+'\" y=\"'+(PT-6)+'\" fill=\"#64748b\" font-size=\"10\" text-anchor=\"end\">dashed = target (passive target = security)</text></svg>';\n}\nfunction trajectoryCards(tj){\n  const esc = (s)=>String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');\n  const usd = (x)=>'$'+Math.round(Number(x)||0).toLocaleString('en-US');\n  const a = tj[tj.length-2]||{}, b = tj[tj.length-1]||{};\n  const card = (name,key,color,fmt)=>{ const cur=Number(b[key])||0, prev=Number(a[key])||0, dv=cur-prev;\n    const dir = dv>0?'▲':(dv<0?'▼':'—'), dcol = dv>0?'#34d399':(dv<0?'#f87171':'#94a3b8');\n    return '<div class=\"card mini\"><div class=\"mlabel\">'+name+'</div><div class=\"mval\" style=\"color:'+color+'\">'+fmt(cur)+'</div><div class=\"mdelta\" style=\"color:'+dcol+'\">'+dir+' '+fmt(Math.abs(dv))+' vs '+esc(a.monthLabel||'prev')+'</div></div>'; };\n  return [ card('Savings','savings','#34d399',usd),\n    card('Training','training','#f59e0b',usd), card('Passive','passive','#a78bfa',usd),\n    card('Savings rate','savingsRate','#e2e8f0',(x)=>Math.round(x)+'%') ].join('');\n}\n\nconst d = $('Parse Agent').first().json;\nconst m = d.metrics || {};\nconst comp = d._completion || {total:0,done:0,pct:0,completed:[],incomplete:[]};\nconst date = d._date; const period = d._period;\nconst stage = String(d.stage||'security').toLowerCase();\nconst enc = encodeURIComponent;\nconst clamp = (x)=>Math.max(0,Math.min(100,Math.round(Number(x)||0)));\nconst inc = clamp(m.income!=null?m.income:100), exp = clamp(m.expense), lia = clamp(m.liabilities);\nconst hl = (Array.isArray(d.highlights)&&d.highlights.length?d.highlights:['savings']).join(',');\nconst iframe = `https://calculator.familywealth.in/embed.html?stage=${enc(stage)}&income=${inc}&expense=${exp}&liabilities=${lia}&highlights=${enc(hl)}`;\nconst esc = (s)=>String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');\nconst tj = d._trajectory || [];\nconst trajCard = tj.length ? `<div class=\"card\"><h2>Financial Security Trajectory</h2>${trajectorySVG(tj)}<div class=\"cards\" style=\"margin-top:12px\">${trajectoryCards(tj)}</div></div>` : '';\nlet progressLine = '';\nif (d._progressStatement) progressLine = `<div class=\"card\"><h2>This Week</h2><p>${esc(d._progressStatement)}</p></div>`;\nconst li = (arr,cls)=> (arr||[]).map(b=>`<li class=\"${cls}\">${esc(b)}</li>`).join('');\nconst label = esc(d.stageLabel || stage);\nconst adv = d.advanced ? `<span class=\"badge\" style=\"background:#16a34a\">Stage advanced!</span>` : '';\nconst html = `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>CFO Reflection</title>\n<style>body{margin:0;background:#0f172a;color:#e2e8f0;font-family:system-ui,-apple-system,sans-serif;padding:24px}\n.wrap{max-width:780px;margin:0 auto}.card{background:#1e293b;border:1px solid #334155;border-radius:12px;padding:20px;margin-bottom:16px}\nh1,h2{color:#6366f1;margin:.2em 0}h2{color:#93c5fd;font-size:1rem}.badge{display:inline-block;background:#6366f1;color:#fff;border-radius:999px;padding:4px 14px;font-weight:600;text-transform:capitalize;margin-left:8px}\niframe{display:block;width:100%;height:420px;border:0;background:transparent}\n.bar{height:14px;border-radius:999px;background:#334155;overflow:hidden;margin:10px 0}.bar>div{height:100%;background:#16a34a}\nul{padding-left:20px}li{margin:6px 0}li.done{color:#86efac}li.todo{color:#94a3b8;text-decoration:line-through}\n.metrics span{display:inline-block;margin-right:16px;color:#cbd5e1}\n.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:12px}\n.mini{padding:14px;margin:0}.mlabel{color:#94a3b8;font-size:.78rem;text-transform:uppercase;letter-spacing:.04em}\n.mval{font-size:1.35rem;font-weight:700;margin:4px 0}.mdelta{font-size:.8rem}</style></head>\n<body><div class=\"wrap\">\n<div class=\"card\" style=\"text-align:center\"><h1>Daily Reflection</h1><p><span class=\"badge\">${label}</span>${adv}</p>\n<iframe src=\"${iframe}\" loading=\"lazy\"></iframe>\n<p class=\"metrics\"><span>Expense ${exp}%</span><span>Liabilities ${lia}%</span><span>Savings ${clamp(m.savings)}%</span><span>Passive ${clamp(m.passive)}%</span></p></div>\n${trajCard}\n<div class=\"card\"><h2>Progress: ${comp.done}/${comp.total} done (${comp.pct}%)</h2>\n<div class=\"bar\"><div style=\"width:${comp.pct}%\"></div></div>\n${comp.completed&&comp.completed.length?`<ul>${li(comp.completed,'done')}</ul>`:''}\n${comp.incomplete&&comp.incomplete.length?`<h2 style=\"font-size:1rem;color:#94a3b8\">Still open</h2><ul>${li(comp.incomplete,'todo')}</ul>`:''}</div>\n<div class=\"card\"><h2>Today</h2><p>${esc(d.completedSummary)}</p></div>\n<div class=\"card\"><h2>Next Step</h2><p>${esc(d.nextStep)}</p></div>\n${progressLine}\n</div></body></html>`;\nreturn [{ json: { html, date, period } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2000,
        330
      ],
      "id": "build-html",
      "name": "Build HTML"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://aicivz8c3l.execute-api.ap-south-1.amazonaws.com/dev/mcp/call",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "X-API-Key",
              "value": "={{ $('API Key').item.json.apiKey }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\n  \"params\":{\"name\":\"graphql_query\",\"arguments\":{\n    \"query\":\"mutation SetReward($id: ID!, $reward: String) { updateGoalItemReward(id: $id, reward: $reward) { id } }\",\n    \"variables\":{ \"id\":\"{{ $('Webhook').item.json.query.goal_id }}\", \"reward\": {{ JSON.stringify($('Build HTML').first().json.html) }} }\n  }},\"id\":1\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2220,
        330
      ],
      "id": "set-reward",
      "name": "Set Reward"
    },
    {
      "parameters": {
        "respondWith": "text",
        "responseBody": "={{ $('Build HTML').first().json.html }}",
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "text/html; charset=utf-8"
              }
            ]
          }
        }
      },
      "id": "937cbcb7-8dbf-46e9-9181-22883e2194e9",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        2160,
        120
      ]
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "API Key",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "API Key": {
      "main": [
        [
          {
            "node": "Get Goal",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Goal": {
      "main": [
        [
          {
            "node": "Parse Goal",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Goal": {
      "main": [
        [
          {
            "node": "Evaluate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Evaluate": {
      "main": [
        [
          {
            "node": "Get Progress",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Progress": {
      "main": [
        [
          {
            "node": "Read CAS",
            "type": "main",
            "index": 0
          },
          {
            "node": "Read Last3Months",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read CAS": {
      "main": [
        [
          {
            "node": "Merge Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Last3Months": {
      "main": [
        [
          {
            "node": "Merge Sheets",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Sheets": {
      "main": [
        [
          {
            "node": "Build Context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Context": {
      "main": [
        [
          {
            "node": "CFO Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CFO Agent": {
      "main": [
        [
          {
            "node": "Parse Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Agent": {
      "main": [
        [
          {
            "node": "Build HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build HTML": {
      "main": [
        [
          {
            "node": "Set Reward",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Reward": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate"
  },
  "staticData": null,
  "pinData": {},
  "tags": [
    {
      "updatedAt": "2026-05-31T13:28:53.668Z",
      "createdAt": "2026-05-31T13:28:53.668Z",
      "id": "EtERIAA5VrQx7Xb2",
      "name": "pillar:cfo"
    },
    {
      "updatedAt": "2026-05-31T13:28:52.809Z",
      "createdAt": "2026-05-31T13:28:52.809Z",
      "id": "kLQMuII6E7BC8apU",
      "name": "evolution"
    },
    {
      "updatedAt": "2026-05-31T13:28:52.950Z",
      "createdAt": "2026-05-31T13:28:52.950Z",
      "id": "vkEv2UGPNBfyH74Y",
      "name": "phase:end"
    }
  ]
}