{
  "name": "ICP & TAM Definition Engine",
  "nodes": [
    {
      "id": "34d203b8-da69-40d2-b933-e1481c0bef53",
      "name": "Quarterly ICP Review",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        112
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 9 1 1,4,7,10 *"
            }
          ]
        }
      }
    },
    {
      "id": "c27feb87-655f-470f-a2ad-63ea49d7548c",
      "name": "Fetch Closed Deals",
      "type": "n8n-nodes-base.salesforce",
      "typeVersion": 1.1,
      "position": [
        224,
        112
      ],
      "parameters": {
        "resource": "search",
        "operation": "query",
        "query": "SELECT Id, Name, StageName, IsWon, Amount, CloseDate, Account.Id, Account.Name, Account.Industry, Account.NumberOfEmployees, Account.AnnualRevenue, Account.Website FROM Opportunity WHERE IsClosed = true ORDER BY Account.Name"
      },
      "credentials": {
        "salesforceOAuth2Api": {
          "id": "553Tae1kPe5Wpthq",
          "name": "Salesforce account"
        }
      }
    },
    {
      "id": "a134d78c-512f-4c35-820b-78f5cc7ecad6",
      "name": "Fetch Clay Enrichment",
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        448,
        112
      ],
      "parameters": {
        "resource": "row",
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "id",
          "value": "Uih8u82Pk1H2idvy",
          "cachedResultName": "Clay Firmographic Enrichment"
        },
        "returnAll": true
      },
      "executeOnce": true
    },
    {
      "id": "9af02ba3-36bd-47fb-997f-f176497cb9c2",
      "name": "Compute ICP Evidence",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        672,
        112
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// ICP evidence: resolve firmographics per account, then compute win rate by trait.\nconst deals = $('Fetch Closed Deals').all().map(i => i.json);\nconst clayRows = $('Fetch Clay Enrichment').all().map(i => i.json);\n\n// Accounts whose NumberOfEmployees / AnnualRevenue were SEEDED into Salesforce for this demo\n// (Clay could not resolve them: fictional demo domains). Seed rule: avg closed-deal size x 0.012\n// employees, with a fixed per-name jitter; revenue = employees x $180k. The rule never looks at win/loss.\nconst SEEDED_FIRMOGRAPHICS_ACCOUNT_IDS = new Set(['001fj00001hXZYnAAO','001fj00001fxWwbAAE','001fj00001hAR5iAAG','001fj00001hAreTAAS','001fj00001hU26EAAS','001fj00001pL8MUAA0','001fj00001h9oFdAAI','001fj00001hXZfFAAW','001fj00001h7eoxAAA','001fj00001hArg5AAC','001fj00001fxWyDAAU','001fj00001hArkvAAC','001fj00001hTW3OAAW','001fj00001hALd1AAG','001fj00001hArmXAAS','001fj00001gMCjNAAW','001fj00001hV9TyAAK','001fj00001h9oE1AAI','001fj00001fxWzpAAE','001fj00001hUydQAAS','001fj00001hAro9AAC','001fj00001pOoqcAAC','001fj00001h765qAAA','001fj00001h8BmcAAE','001fj00001hVv8oAAC','001fj00001h9oIrAAI','001fj00001hArcrAAC','001fj00001hArhhAAC','001fj00001hXZaPAAW','001fj00001h6yugAAA','001fj00001hXZc1AAG']);\n\nconst GENERIC = new Set(['inc','corp','corporation','co','company','llc','llp','ltd','limited','private','group','the','of','and','partners','systems','technologies','holdings','plc']);\nfunction toks(s) { return new Set((s || '').toLowerCase().replace(/[^a-z0-9 ]/g, ' ').split(/\\s+/).filter(t => t && !GENERIC.has(t))); }\nfunction jaccard(a, b) { const A = toks(a), B = toks(b); if (!A.size || !B.size) return 0; let i = 0; for (const t of A) if (B.has(t)) i++; return i / (A.size + B.size - i); }\nfunction industryAgrees(sf, cl) {\n  if (!sf || !cl) return true;\n  const a = sf.toLowerCase(), b = cl.toLowerCase();\n  if (a.includes(b) || b.includes(a)) return true;\n  const B = toks(b); for (const t of toks(a)) if (B.has(t)) return true;\n  return false;\n}\nconst clayByName = {}; for (const r of clayRows) clayByName[(r.accountName || '').toLowerCase()] = r;\n\n// 1) One record per account with its won/lost deal counts\nconst accounts = {};\nfor (const d of deals) {\n  const a = d.Account || {}; if (!a.Id) continue;\n  if (!accounts[a.Id]) accounts[a.Id] = { id: a.Id, name: a.Name, industry: a.Industry || 'Unknown', sfEmployees: a.NumberOfEmployees, sfRevenue: a.AnnualRevenue, won: 0, lost: 0, wonAmount: 0 };\n  const acc = accounts[a.Id];\n  if (d.IsWon) { acc.won++; acc.wonAmount += (d.Amount || 0); } else acc.lost++;\n}\n\n// 2) Resolve firmographics: accept a Clay match only if it is the same company\nconst clayAudit = { checked: 0, notFound: 0, invalid: 0, noDomain: 0, matchedAccepted: 0, matchedRejected: [] };\nfor (const acc of Object.values(accounts)) {\n  const c = clayByName[acc.name.toLowerCase()];\n  acc.firmoSource = 'none';\n  if (c) {\n    if (c.clayResult === 'no_domain') clayAudit.noDomain++; else clayAudit.checked++;\n    if (c.clayResult === 'not_found') clayAudit.notFound++;\n    if (c.clayResult === 'invalid_domain') clayAudit.invalid++;\n    if (c.clayResult === 'matched') {\n      const sim = jaccard(acc.name, c.clayMatchedName);\n      const indOk = industryAgrees(acc.industry === 'Unknown' ? '' : acc.industry, c.clayIndustry);\n      if (sim >= 0.5 && indOk && c.clayEmployees) {\n        clayAudit.matchedAccepted++; acc.employees = c.clayEmployees; acc.firmoSource = 'clay';\n      } else {\n        clayAudit.matchedRejected.push(acc.name + ' -> Clay returned \"' + c.clayMatchedName + '\" (' + (c.clayIndustry || 'n/a') + '); name similarity ' + sim.toFixed(2) + (indOk ? '' : ', industry mismatch vs Salesforce ' + acc.industry));\n      }\n    }\n  }\n  if (acc.firmoSource === 'none' && acc.sfEmployees) {\n    acc.employees = acc.sfEmployees;\n    acc.firmoSource = SEEDED_FIRMOGRAPHICS_ACCOUNT_IDS.has(acc.id) ? 'salesforce_seeded' : 'salesforce';\n  }\n  if (!acc.revenue) acc.revenue = acc.sfRevenue || null;\n}\n\nfunction empBand(e) { if (!e) return 'Unknown'; return e < 250 ? '<250 employees' : e < 500 ? '250-499 employees' : e < 1000 ? '500-999 employees' : '1,000+ employees'; }\nfunction revBand(r) { if (!r) return 'Unknown'; return r < 50e6 ? '<$50M revenue' : r < 150e6 ? '$50M-$150M revenue' : '$150M+ revenue'; }\n\nconst accList = Object.values(accounts);\nconst W = accList.reduce((s, a) => s + a.won, 0), L = accList.reduce((s, a) => s + a.lost, 0), N = W + L;\nconst baseline = W / N;\nconst MIN_DEALS = 5;\nconst MIN_ACCOUNTS = 3; // one big account's repeat deals are not independent evidence\n\n// 3) Win rate by trait, with a two-proportion z-score (segment vs everyone else)\nfunction segmentStats(dimension, keyFn) {\n  const g = {};\n  for (const a of accList) { const k = keyFn(a); if (!g[k]) g[k] = { won: 0, lost: 0, accounts: 0, wonAmount: 0 }; g[k].won += a.won; g[k].lost += a.lost; g[k].accounts++; g[k].wonAmount += a.wonAmount; }\n  return Object.entries(g).map(([segment, s]) => {\n    const n = s.won + s.lost, rate = s.won / n;\n    const restW = W - s.won, restN = N - n;\n    let z = null;\n    if (restN > 0) { const p = W / N; const se = Math.sqrt(p * (1 - p) * (1 / n + 1 / restN)); z = se > 0 ? (rate - restW / restN) / se : null; }\n    const strength = n < MIN_DEALS ? 'too few deals' : s.accounts < MIN_ACCOUNTS ? 'too few accounts' : (z !== null && Math.abs(z) >= 1.96 ? 'significant' : (z !== null && Math.abs(z) >= 1.64 ? 'directional' : 'weak'));\n    return { dimension, segment, deals: n, won: s.won, lost: s.lost, accounts: s.accounts, winRate: Math.round(rate * 1000) / 10, liftPts: Math.round((rate - baseline) * 1000) / 10, zScore: z === null ? null : Math.round(z * 100) / 100, strength, avgWonDeal: s.won ? Math.round(s.wonAmount / s.won) : 0 };\n  }).sort((a, b) => b.winRate - a.winRate);\n}\nconst stats = [\n  ...segmentStats('industry', a => a.industry),\n  ...segmentStats('employees', a => empBand(a.employees)),\n  ...segmentStats('revenue', a => revBand(a.revenue)),\n];\nconst wonDeals = deals.filter(d => d.IsWon);\n// Median, not mean: one $2.1M sample-data deal would otherwise inflate every TAM dollar\nconst wonAmts = wonDeals.map(d => d.Amount || 0).sort((a, b) => a - b);\nconst avgWonDeal = wonAmts.length % 2 ? wonAmts[(wonAmts.length - 1) / 2] : Math.round((wonAmts[wonAmts.length / 2 - 1] + wonAmts[wonAmts.length / 2]) / 2);\nconst sourceCounts = accList.reduce((m, a) => { m[a.firmoSource] = (m[a.firmoSource] || 0) + 1; return m; }, {});\n\nfunction line(s) { return '- [' + s.dimension + '] ' + s.segment + ': ' + s.won + 'W/' + s.lost + 'L (' + s.deals + ' deals, ' + s.accounts + ' accts) = ' + s.winRate + '% win rate, ' + (s.liftPts >= 0 ? '+' : '') + s.liftPts + ' pts vs baseline, z=' + s.zScore + ', ' + s.strength; }\nconst statsText = stats.map(line).join('\\n');\nconst provenance = 'Firmographic source per account: ' + JSON.stringify(sourceCounts) + '. Clay live enrichment: ' + clayAudit.checked + ' domains checked, ' + clayAudit.notFound + ' Company Not Found, ' + clayAudit.invalid + ' invalid domain, ' + clayAudit.noDomain + ' accounts had no website to check, ' + clayAudit.matchedAccepted + ' matches accepted, ' + clayAudit.matchedRejected.length + ' matches REJECTED as the wrong company: ' + clayAudit.matchedRejected.join(' | ');\n\nreturn [{ json: { baselineWinRate: Math.round(baseline * 1000) / 10, totalDeals: N, totalWon: W, totalLost: L, accountCount: accList.length, avgWonDeal, minDeals: MIN_DEALS, minAccounts: MIN_ACCOUNTS, stats, statsText, provenance, clayAudit, sourceCounts } }];\n"
      },
      "executeOnce": true
    },
    {
      "id": "6bed9484-fc40-4b79-a53e-ff603374c90b",
      "name": "ICP Strategist",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [
        896,
        112
      ],
      "parameters": {
        "promptType": "define",
        "text": "=Real closed-deal evidence for this run:\nBaseline: {{ $json.totalWon }} won / {{ $json.totalLost }} lost across {{ $json.accountCount }} accounts = {{ $json.baselineWinRate }}% win rate.\nMedian real won deal: ${{ $json.avgWonDeal }}.\nSegments need at least {{ $json.minDeals }} deals and {{ $json.minAccounts }} accounts to count.\n\nWin rate by segment:\n{{ $json.statsText }}\n\nData provenance:\n{{ $json.provenance }}\n\nDraft the ICP definition and TAM estimate.",
        "hasOutputParser": true,
        "options": {
          "systemMessage": "You are the ICP & TAM analyst for Fortavault, Inc., a fictional data protection and security platform. You are given win/loss statistics that were COMPUTED from real closed Salesforce deals, broken out by industry, employee band, and revenue band, each with a z-score comparing that segment against all other deals. Your job is to turn that evidence into an ideal customer profile a sales team could actually use.\n\nRules:\n- Pick 2 or 3 criteria total. Only use segments whose strength is 'significant', 'directional' or 'weak' (never 'too few deals' or 'too few accounts'). Prefer the segments with the largest absolute z-scores.\n- A criterion may be an include (\"sell here\") or an exclude (\"deprioritize this\"). If the strongest statistical evidence is negative, say so plainly: an exclusion backed by a significant z-score is more useful than an inclusion backed by a weak one.\n- Every criterion's evidence must quote the actual numbers (won/lost, win rate, baseline, z-score) and state its strength honestly. Do not call a 'weak' or 'directional' result proven.\n- If a whole dimension (e.g. company size) shows no meaningful separation, say that it does NOT predict winning in this data rather than inventing a size criterion.\n- For TAM, estimate how many US companies plausibly fit the included industry criteria (use your general knowledge of US business counts; restrict to companies large enough to buy an enterprise security platform, roughly 200+ employees). Give a single integer companyCountEstimate and state your assumptions in one or two sentences. Do NOT multiply by deal size yourself; the workflow computes the dollar figure deterministically from the real median won deal. Do NOT attribute the count to any named dataset or source (Census, BLS, etc.): you have not been given one, so describe it as a rough knowledge-based estimate.\n- caveats: 1-2 short sentences (under 350 characters total) on the limits of this data (sample size, seeded firmographics, the rejected Clay matches)."
        }
      }
    },
    {
      "id": "46b8ff41-f401-4044-8151-7e068269a754",
      "name": "Claude Sonnet",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.3,
      "position": [
        912,
        336
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "claude-sonnet-4-6",
          "cachedResultName": "Claude Sonnet 4.6"
        },
        "options": {
          "temperature": 0.4
        }
      },
      "credentials": {
        "anthropicApi": {
          "id": "3d3ZiTi7BwnkFwqU",
          "name": "Anthropic account"
        }
      }
    },
    {
      "id": "0bc9a1a0-68f9-45df-8d9b-c31c8ba75dfd",
      "name": "ICP Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "typeVersion": 1.3,
      "position": [
        1040,
        336
      ],
      "parameters": {
        "schemaType": "fromJson",
        "jsonSchemaExample": "{ \"icpSummary\": \"3-4 sentence plain-English ICP definition grounded in the numbers\", \"criteria\": [ { \"rank\": 1, \"dimension\": \"industry\", \"segment\": \"exact segment name as given, e.g. Retail\", \"criterion\": \"short rule, e.g. Deprioritize Retail\", \"criterionType\": \"exclude\", \"evidence\": \"1-2 sentences quoting the real won/lost, win rate vs baseline, z-score and strength\" } ], \"tam\": { \"companyCountEstimate\": 4200, \"assumptions\": \"1-2 sentences\" }, \"caveats\": \"1-2 sentences\" }"
      }
    },
    {
      "id": "febd1b73-e39e-4e8c-a274-ed13b733c697",
      "name": "Build ICP Log Rows",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1248,
        16
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const ev = $('Compute ICP Evidence').first().json;\nconst out = $('ICP Strategist').first().json.output;\nconst runId = 'icp-' + $now.toFormat('yyyyLLdd-HHmm');\nconst count = Number(out.tam.companyCountEstimate) || 0;\nconst tamUsd = Math.round(count * ev.avgWonDeal);\nfunction findStat(dim, seg) {\n  const d = (dim || '').toLowerCase(), s = (seg || '').toLowerCase();\n  return ev.stats.find(x => x.dimension === d && (s.includes(x.segment.toLowerCase()) || x.segment.toLowerCase().includes(s))) || {};\n}\nconst dataNote = 'Win/loss: ' + ev.totalDeals + ' real closed Salesforce deals. Firmographics: ' + JSON.stringify(ev.sourceCounts) + '. Clay: ' + ev.clayAudit.checked + ' checked live, ' + ev.clayAudit.matchedAccepted + ' accepted, ' + ev.clayAudit.matchedRejected.length + ' rejected as wrong company.';\nreturn out.criteria.map(c => {\n  const st = findStat(c.dimension, c.segment);\n  return { json: {\n    runId, criterionRank: c.rank, dimension: c.dimension, criterion: c.criterion, criterionType: c.criterionType,\n    segmentWinRate: st.winRate ?? null, baselineWinRate: ev.baselineWinRate, segmentDeals: st.deals ?? null, zScore: st.zScore ?? null,\n    evidence: c.evidence, tamEstimateUsd: tamUsd,\n    tamAssumptions: count.toLocaleString() + ' companies x $' + ev.avgWonDeal.toLocaleString() + ' median real won deal. ' + out.tam.assumptions,\n    icpSummary: out.icpSummary, dataSourcesNote: dataNote, loggedAt: $now.toISO()\n  } };\n});\n"
      }
    },
    {
      "id": "01879482-5e7d-4e11-975c-626f79c1c8c7",
      "name": "Log ICP Criteria",
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        1472,
        16
      ],
      "parameters": {
        "resource": "row",
        "operation": "insert",
        "dataTableId": {
          "__rl": true,
          "mode": "id",
          "value": "niD4hm70QGLUzTO1",
          "cachedResultName": "ICP Definitions"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {}
        }
      }
    },
    {
      "id": "0943f7f1-5b87-4d26-8bbc-95f5f103a7e0",
      "name": "Build Slack Summary",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1248,
        208
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const ev = $('Compute ICP Evidence').first().json;\nconst out = $('ICP Strategist').first().json.output;\nconst count = Number(out.tam.companyCountEstimate) || 0;\nconst tamUsd = Math.round(count * ev.avgWonDeal);\nfunction money(n) { return n >= 1e9 ? '$' + (n / 1e9).toFixed(1) + 'B' : n >= 1e6 ? '$' + (n / 1e6).toFixed(0) + 'M' : '$' + n.toLocaleString(); }\n// Truncate on a sentence end if there is one; otherwise on a word boundary. Never mid-word.\nfunction cut(s, max) {\n  if (!s || s.length <= max) return s || '';\n  const t = s.slice(0, max);\n  const i = Math.max(t.lastIndexOf('. '), t.lastIndexOf('.\\u201d '));\n  if (i > 0) return t.slice(0, i + 1);\n  const w = t.lastIndexOf(' ');\n  return (w > 0 ? t.slice(0, w) : t).replace(/[,;:\\s\\u2014-]+$/, '') + '\\u2026';\n}\nfunction findStat(dim, seg) { const d = (dim || '').toLowerCase(), s = (seg || '').toLowerCase(); return ev.stats.find(x => x.dimension === d && (s.includes(x.segment.toLowerCase()) || x.segment.toLowerCase().includes(s))) || {}; }\nconst crit = out.criteria.map(c => {\n  const st = findStat(c.dimension, c.segment);\n  const icon = c.criterionType === 'exclude' ? ':no_entry_sign:' : ':white_check_mark:';\n  return icon + ' *' + c.criterion + '* — ' + (st.won ?? '?') + 'W/' + (st.lost ?? '?') + 'L, ' + (st.winRate ?? '?') + '% vs ' + ev.baselineWinRate + '% baseline (z=' + (st.zScore ?? 'n/a') + ', ' + (st.strength || 'n/a') + ')';\n}).join('\\n');\nconst text = ':dart: *Quarterly ICP & TAM Definition — Fortavault*\\n' +\n  'Built from ' + ev.totalDeals + ' real closed Salesforce deals (' + ev.totalWon + ' won / ' + ev.totalLost + ' lost, ' + ev.baselineWinRate + '% baseline win rate).\\n\\n' +\n  cut(out.icpSummary, 600) + '\\n\\n*ICP criteria (each backed by computed win-rate evidence):*\\n' + crit +\n  '\\n\\n*Rough TAM:* ~' + money(tamUsd) + ' = ~' + count.toLocaleString() + ' companies x ' + money(ev.avgWonDeal) + ' median real won deal. _' + cut(out.tam.assumptions, 350) + '_' +\n  '\\n\\n*Data honesty:* Clay was run live on ' + ev.clayAudit.checked + ' account domains: ' + ev.clayAudit.notFound + ' not found, ' + ev.clayAudit.matchedRejected.length + ' \"matches\" rejected as the wrong company, ' + ev.clayAudit.matchedAccepted + ' accepted. Company size therefore comes from Salesforce (seeded for Fortavault\\'s demo accounts). ' + cut(out.caveats, 400) +\n  '\\n\\nLogged to the *ICP Definitions* Data Table for Lead Grading (Day 18) to reuse.';\nreturn [{ json: { slackText: text.slice(0, 2900), tamUsd } }];\n"
      }
    },
    {
      "id": "48cf9180-3e3d-458d-8caa-dea26e60de8d",
      "name": "Post ICP to Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.7,
      "position": [
        1472,
        208
      ],
      "parameters": {
        "resource": "message",
        "operation": "post",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "C0BQ3G96HL6",
          "cachedResultName": "general"
        },
        "text": "={{ $json.slackText }}",
        "otherOptions": {
          "includeLinkToWorkflow": false
        }
      },
      "webhookId": "8d3af27e-1a70-41e1-9036-b6f9357aabaa",
      "credentials": {
        "slackApi": {
          "id": "mv7TIi8kucFPqPg6",
          "name": "Slack account"
        }
      }
    }
  ],
  "connections": {
    "Quarterly ICP Review": {
      "main": [
        [
          {
            "node": "Fetch Closed Deals",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Closed Deals": {
      "main": [
        [
          {
            "node": "Fetch Clay Enrichment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Clay Enrichment": {
      "main": [
        [
          {
            "node": "Compute ICP Evidence",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compute ICP Evidence": {
      "main": [
        [
          {
            "node": "ICP Strategist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ICP Strategist": {
      "main": [
        [
          {
            "node": "Build ICP Log Rows",
            "type": "main",
            "index": 0
          },
          {
            "node": "Build Slack Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude Sonnet": {
      "ai_languageModel": [
        [
          {
            "node": "ICP Strategist",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "ICP Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "ICP Strategist",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Build ICP Log Rows": {
      "main": [
        [
          {
            "node": "Log ICP Criteria",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Slack Summary": {
      "main": [
        [
          {
            "node": "Post ICP to Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "instanceId": "hainehainehaine"
  }
}