{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "9f5dac0c-0427-4d4e-a49d-0c55898574e2",
      "name": "Note: Database Setup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        -7312
      ],
      "parameters": {
        "color": "#97A3D3",
        "width": 1088,
        "height": 468,
        "content": "## \ud83d\uddc4\ufe0f DATABASE SETUP: Initialize `Team_Config`                                        \n\n**Run this ONCE** \n\n1. It creates an n8n Data Table called `Team_Config`.\n2. It defines the exact columns needed for AgentMail hierarchy enforcement.\n3. It injects the default 3-tier system (AE, BDR, Marketing).\n\nAfter running this, go to **Data (left menu) -> Team_Config** in n8n to view and edit your team's email addresses like a spreadsheet."
      },
      "typeVersion": 1
    },
    {
      "id": "55e35b96-dfa8-402a-805a-89858f84e7e7",
      "name": "1. Create Schema",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        624,
        -7008
      ],
      "parameters": {
        "columns": {
          "column": [
            {
              "name": "tier_id"
            },
            {
              "name": "display_name"
            },
            {
              "name": "inbox_address"
            },
            {
              "name": "client_id"
            },
            {
              "name": "priority",
              "type": "number"
            },
            {
              "name": "escalates_to"
            },
            {
              "name": "decay_days",
              "type": "number"
            }
          ]
        },
        "options": {
          "createIfNotExists": true
        },
        "resource": "table",
        "operation": "create",
        "tableName": "Team_Config"
      },
      "typeVersion": 1.1
    },
    {
      "id": "5f1bb5f5-4af9-4a0a-a6c2-b0e72a4f8657",
      "name": "2. Generate Baseline Tiers",
      "type": "n8n-nodes-base.code",
      "position": [
        832,
        -7008
      ],
      "parameters": {
        "jsCode": "return [\n  {\n    json: {\n      tier_id: 'ae',\n      display_name: 'Account Executive',\n      inbox_address: 'user@example.com',\n      client_id: 'tier_ae',\n      priority: 3,\n      escalates_to: ''\n    }\n  },\n  {\n    json: {\n      tier_id: 'bdr',\n      display_name: 'BDR / SDR',\n      inbox_address: 'user@example.com',\n      client_id: 'tier_bdr',\n      priority: 2,\n      escalates_to: 'ae'\n    }\n  },\n  {\n    json: {\n      tier_id: 'marketing',\n      display_name: 'Marketing',\n      inbox_address: 'user@example.com',\n      client_id: 'tier_mkt',\n      priority: 1,\n      escalates_to: 'bdr'\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "8648dbfe-5781-4900-99db-7573b4bbeb39",
      "name": "3. Populate Database",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        1056,
        -7008
      ],
      "parameters": {
        "columns": {
          "value": {},
          "column": [
            {
              "name": "={{ $json.tier_id }}"
            },
            {
              "name": "={{ $json.display_name }}"
            }
          ],
          "schema": [
            {
              "id": "tier_id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "tier_id",
              "defaultMatch": false
            },
            {
              "id": "display_name",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "display_name",
              "defaultMatch": false
            },
            {
              "id": "inbox_address",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "inbox_address",
              "defaultMatch": false
            },
            {
              "id": "client_id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "client_id",
              "defaultMatch": false
            },
            {
              "id": "priority",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "priority",
              "defaultMatch": false
            },
            {
              "id": "escalates_to",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "escalates_to",
              "defaultMatch": false
            },
            {
              "id": "decay_days",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "decay_days",
              "defaultMatch": false
            }
          ],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "optimizeBulk": false
        },
        "dataTableId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('1. Create Schema').item.json.id }}"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "1b338914-e5f8-4d96-8b4e-ca446dfe0667",
      "name": "GET Block Lists",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1056,
        -5520
      ],
      "parameters": {
        "url": "={{ 'https://api.agentmail.to/v0/inboxes/' + $json.inbox_address + '/lists/send/block' }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "a83b274a-86a9-4e11-a801-d6af3557ec9f",
      "name": "\ud83d\udcca Compile Audit Report",
      "type": "n8n-nodes-base.code",
      "position": [
        1280,
        -5520
      ],
      "parameters": {
        "jsCode": "const responses = $input.all();\nconst configRows = $('\u2699\ufe0f Team_Config').all();\nconst report = [];\nlet totalBlocked = 0;\n\nfor (let i = 0; i < responses.length; i++) {\n  const meta = configRows[i] ? configRows[i].json : {};\n  const resp = responses[i].json;\n  const entries = Array.isArray(resp.entries) ? resp.entries : Array.isArray(resp) ? resp : [];\n\n  report.push({\n    inbox_id: meta.inbox_address,\n    tier_id: meta.tier_id,\n    display_name: meta.display_name,\n    priority: meta.priority,\n    total_blocked: entries.length,\n    blocked_prospects: entries.map(e => ({\n      prospect: e.entry || 'unknown',\n      reason: e.reason || 'No reason',\n      blocked_at: e.created_at || 'unknown'\n    }))\n  });\n  totalBlocked += entries.length;\n}\n\nreport.sort((a, b) => (b.priority || 0) - (a.priority || 0));\n\nreturn [{ json: {\n  status: 'AUDIT_COMPLETE',\n  total_inboxes: report.length,\n  total_active_blocks: totalBlocked,\n  report,\n  generated_at: new Date().toISOString()\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "c279f8aa-dce6-4462-81e4-9aa7173e43e9",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "disabled": true,
      "position": [
        -288,
        -6528
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "483ccf8a-0dcb-4070-b653-23a644c8c003",
      "name": "\ud83d\udd27 Build Config",
      "type": "n8n-nodes-base.code",
      "position": [
        496,
        -5904
      ],
      "parameters": {
        "jsCode": "// ====================================================\n// \u2699\ufe0f ONLY EDIT THIS \u2014 your n8n instance URL\n// ====================================================\n\nconst n8n_url = 'https://stuctstunter.zeabur.app';\n\n// ====================================================\n// \ud83d\uded1 DO NOT EDIT BELOW\n// ====================================================\n\nconst rows = $input.all().map(i => i.json);\n\nif (rows.length === 0) {\n  throw new Error('Team_Config DataTable is empty. Run Layer 0A first.');\n}\n\nconst config = { n8n_url };\n\nfor (const row of rows) {\n  const tier = (row.tier_id || '').toLowerCase().trim();\n  config[tier + '_inbox'] = row.inbox_address;\n  config[tier + '_client_id'] = row.client_id;\n  config[tier + '_display_name'] = row.display_name;\n  config[tier + '_priority'] = row.priority;\n  config[tier + '_escalates_to'] = row.escalates_to || '';\n}\n\nreturn [{ json: config }];"
      },
      "typeVersion": 2
    },
    {
      "id": "c757b8b4-9fe6-406f-8607-180913fac2fb",
      "name": "Note: Layer 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        -6160
      ],
      "parameters": {
        "width": 1088,
        "height": 520,
        "content": "## \ud83c\udfd7\ufe0f LAYER 1: Registering the Webhook \n\n**Reads Team_Config DataTable \u2014 same source as Layer 0A.**\n\nBuilds config from DataTable rows, checks webhooks, registers missing ones.\n\nSet your n8n URL here\n"
      },
      "typeVersion": 1
    },
    {
      "id": "3670ef9a-a9fd-410c-9449-8cf6efd65dce",
      "name": "\u2699\ufe0f Load Team_Config",
      "type": "n8n-nodes-base.dataTable",
      "disabled": true,
      "position": [
        -48,
        -6528
      ],
      "parameters": {
        "limit": 20,
        "filters": {
          "conditions": [
            {
              "condition": "isNotEmpty"
            }
          ]
        },
        "orderBy": true,
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "ZPtmczWG8ZNDbAS6",
          "cachedResultUrl": "/projects/2qxSXp3XpPDzCYbp/datatables/ZPtmczWG8ZNDbAS6",
          "cachedResultName": "Team_Config"
        },
        "orderByColumn": "priority"
      },
      "typeVersion": 1.1
    },
    {
      "id": "6376c8c7-4891-4e51-9670-f4220de4b7a2",
      "name": "Note: Layer ",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        -5648
      ],
      "parameters": {
        "color": 4,
        "width": 1088,
        "height": 376,
        "content": "## \ud83d\udcca LAYER 4: Audit Dashboard\n\n**Purpose:** Pull all block lists across all 3 inboxes. See who is blocked, why, and when.\n\n**When to run:**\n- After any enforcement to verify\n- Scheduled (cron) for daily reports\n- On-demand by SDR managers\n\n**Endpoint:** `GET /v0/inboxes/{inbox_id}/lists/send/block`\n\n**This replaces looking at HubSpot.**\nThe block list IS the source of truth for email permissions."
      },
      "typeVersion": 1
    },
    {
      "id": "eabf5591-1fbd-44fa-ad08-43df539436cb",
      "name": "Note: Decay Timer",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1568,
        -7312
      ],
      "parameters": {
        "color": 3,
        "width": 1904,
        "height": 468,
        "content": "## \u23f3 LAYER 2: Decay Timer\n\n**Runs every Sunday at midnight.**\n\nReads Team_Config DataTable for inbox addresses AND decay windows.\nMAKE sure you setup the database first\nThe DataTable returns one row per inbox \u2014 that IS the fan out.\n\n```\nSTEP 1 \u2192 Read Team_Config (inbox + decay_days per tier)\nSTEP 2 \u2192 GET block list for each inbox\nSTEP 3 \u2192 Find entries older than that tier's decay_days\nSTEP 4 \u2192 DELETE expired blocks\nSTEP 5 \u2192 Compile report\n```\n\nPer-tier decay means AE blocks can last longer than Marketing blocks.\nEdit decay_days in the DataTable to change windows."
      },
      "typeVersion": 1
    },
    {
      "id": "b7c21d76-7603-4985-9ad7-95f27dc78e8d",
      "name": "\u2699\ufe0f Read Team_Config",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        1840,
        -6992
      ],
      "parameters": {
        "limit": 20,
        "filters": {
          "conditions": [
            {
              "condition": "isNotEmpty"
            }
          ]
        },
        "orderBy": true,
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "ZPtmczWG8ZNDbAS6",
          "cachedResultUrl": "/projects/2qxSXp3XpPDzCYbp/datatables/ZPtmczWG8ZNDbAS6",
          "cachedResultName": "Team_Config"
        },
        "orderByColumn": "priority"
      },
      "typeVersion": 1.1
    },
    {
      "id": "026be386-12c1-48c4-a032-fe1eac47c6a7",
      "name": "\ud83d\udcca Decay Report",
      "type": "n8n-nodes-base.code",
      "position": [
        2976,
        -7152
      ],
      "parameters": {
        "jsCode": "const removed = $input.all().map(i => i.json);\n\nconst byTier = {};\nfor (const item of removed) {\n  const tier = item.tier_id || 'unknown';\n  if (!byTier[tier]) byTier[tier] = { display_name: item.display_name, count: 0, prospects: [] };\n  byTier[tier].count++;\n  byTier[tier].prospects.push(item.prospect);\n}\n\nconst summaryLines = Object.entries(byTier).map(([tier, data]) => \n  '- ' + data.display_name + ' (' + tier + '): ' + data.count + ' leads released'\n);\n\nreturn [{ json: {\n  status: 'DECAY_COMPLETE',\n  total_released: removed.length,\n  by_tier: byTier,\n  message: 'Decay Timer Complete. Released ' + removed.length + ' prospects back into the TAM.',\n  summary: summaryLines.join('\\n'),\n  timestamp: new Date().toISOString()\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "286ff967-3c16-443c-9929-d4d4bcdd0b5e",
      "name": "\ud83d\udccb All Clear",
      "type": "n8n-nodes-base.code",
      "position": [
        2752,
        -6976
      ],
      "parameters": {
        "jsCode": "return [{ json: { status: 'CLEAN', message: 'All block lists within decay limits. Nothing to clear.', timestamp: new Date().toISOString() } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "b4f1b85e-ddd2-4420-8786-7de9c207c7d5",
      "name": "Note: Overview1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        -6848
      ],
      "parameters": {
        "color": 7,
        "width": 1824,
        "height": 700,
        "content": "# \ud83c\udfd7\ufe0f LAYER 0: Inbox Setup \n**Reads config from Team_Config DataTable.**\nNo hardcoded inbox names anywhere.\n\n```\nSTEP 1 \u2192 Read Team_Config table (source of truth)\nSTEP 2 \u2192 Fetch live inboxes from AgentMail\nSTEP 3 \u2192 Compare + tier check\nSTEP 4 \u2192 Route: ALL_MATCHED / CREATE / TIER_BLOCKED\nSTEP 5 \u2192 Create missing / Report\n```\n\nEdit the **Team_Config DataTable** to change inboxes.\nEdit **plan_tier** in the Compare node if you upgrade."
      },
      "typeVersion": 1
    },
    {
      "id": "d019203c-7d6f-4b8a-853c-6c75697e369e",
      "name": "\ud83d\udce5 Fetch Live Inboxes",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        576,
        -6416
      ],
      "parameters": {
        "url": "https://api.agentmail.to/v0/inboxes",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "27fe04bb-8f89-42cb-a5a6-422ad7e7a3f3",
      "name": "\ud83d\udd0d Compare + Tier Check",
      "type": "n8n-nodes-base.code",
      "position": [
        736,
        -6416
      ],
      "parameters": {
        "jsCode": "// ====================================================\n// \u2699\ufe0f PLAN TIER \u2014 only thing you edit here\n// ====================================================\n\nconst plan_tier = 'free';\nconst TIER_LIMITS = { free: 3, pro: 50, enterprise: 500 };\nconst max_inboxes = TIER_LIMITS[plan_tier] || 3;\n\n// ====================================================\n// \ud83d\uded1 DO NOT EDIT BELOW \u2014 reads from DataTable + API\n// ====================================================\n\n// Load Team_Config rows (already one item per row)\nconst configRows = $('\u2699\ufe0f Load Team_Config').all().map(i => i.json);\n\nif (configRows.length === 0) {\n  throw new Error('Team_Config DataTable is empty. Add your inbox rows first.');\n}\n\nif (configRows.length > max_inboxes) {\n  throw new Error('Team_Config has ' + configRows.length + ' inboxes but ' + plan_tier + ' plan allows ' + max_inboxes + '.');\n}\n\n// Build wanted list from DataTable\nconst wanted = configRows.map(row => {\n  const addr = (row.inbox_address || '').toLowerCase().trim();\n  const parts = addr.split('@');\n  return {\n    username: parts[0] || '',\n    domain: parts[1] || 'agentmail.to',\n    display_name: (row.display_name || '').trim(),\n    client_id: (row.client_id || '').trim(),\n    tier_id: (row.tier_id || '').trim(),\n    priority: row.priority || 0,\n    escalates_to: (row.escalates_to || '').trim(),\n    expected_inbox_id: addr\n  };\n});\n\n// Read live inboxes from API\nconst liveResponse = $input.first().json;\nconst liveInboxes = Array.isArray(liveResponse.inboxes) ? liveResponse.inboxes : Array.isArray(liveResponse) ? liveResponse : [];\nconst liveIds = new Set(liveInboxes.map(i => String(i.inbox_id || '').toLowerCase().trim()));\nconst wantedIds = new Set(wanted.map(w => w.expected_inbox_id));\n\nconst matched = [];\nconst missing = [];\n\nfor (const w of wanted) {\n  if (liveIds.has(w.expected_inbox_id)) { matched.push(w); }\n  else { missing.push(w); }\n}\n\nconst orphaned = liveInboxes\n  .filter(i => { const id = String(i.inbox_id || '').toLowerCase().trim(); return id && !wantedIds.has(id); })\n  .map(i => ({ inbox_id: String(i.inbox_id).toLowerCase().trim(), display_name: i.display_name || null, client_id: i.client_id || null }));\n\nconst slots_used = liveInboxes.length;\nconst slots_available = Math.max(0, max_inboxes - slots_used);\nconst freed_if_cleaned = orphaned.length;\nconst slots_after_cleanup = Math.max(0, max_inboxes - (slots_used - freed_if_cleaned));\nconst can_create = missing.length <= slots_available;\nconst can_create_after_cleanup = missing.length <= slots_after_cleanup;\n\nlet action;\nif (missing.length === 0) { action = 'ALL_MATCHED'; }\nelse if (can_create) { action = 'CREATE'; }\nelse { action = 'TIER_BLOCKED'; }\n\nreturn [{ json: { plan: { tier: plan_tier, max_inboxes, slots_used, slots_available, freed_if_cleaned, slots_after_cleanup, can_create, can_create_after_cleanup }, action, wanted_count: wanted.length, live_count: liveInboxes.length, matched_count: matched.length, missing_count: missing.length, orphaned_count: orphaned.length, matched, missing, orphaned } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "a90e4517-d58e-4fab-91f0-2fa48291cb7e",
      "name": "\ud83d\udd00 Route Decision",
      "type": "n8n-nodes-base.switch",
      "position": [
        960,
        -6432
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "all-matched",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.action }}",
                    "rightValue": "ALL_MATCHED"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "create",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.action }}",
                    "rightValue": "CREATE"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "blocked",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.action }}",
                    "rightValue": "TIER_BLOCKED"
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "7d5d6817-2fc5-46f1-af12-1ec082b5dede",
      "name": "\ud83d\udd00 Fan Out Missing",
      "type": "n8n-nodes-base.code",
      "position": [
        1136,
        -6528
      ],
      "parameters": {
        "jsCode": "const compareResult = $('\ud83d\udd0d Compare + Tier Check').first().json;\nconst missing = compareResult.missing || [];\nreturn missing.map(i => ({ json: i }));"
      },
      "typeVersion": 2
    },
    {
      "id": "71f7f095-c0ba-450e-88a5-10abebe99b71",
      "name": "\ud83d\udcec Create Missing Inboxes",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1360,
        -6528
      ],
      "parameters": {
        "url": "https://api.agentmail.to/v0/inboxes",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "jsonBody": "={\n  \"username\": \"{{ $json.username }}\",\n  \"display_name\": \"{{ $json.display_name }}\",\n  \"client_id\": \"{{ $json.client_id }}\"\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "ce8740ad-d746-4abf-a25a-ac22fa3fe49d",
      "name": "\ud83d\udd17 Aggregate Creations",
      "type": "n8n-nodes-base.code",
      "position": [
        1584,
        -6528
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nreturn [{ json: { creation_results: items.map(i => i.json), count: items.length } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "070c63b6-fc41-4031-8a32-f39d679a7d17",
      "name": "\ud83d\udccb Final Fetch",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1808,
        -6464
      ],
      "parameters": {
        "url": "https://api.agentmail.to/v0/inboxes",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "1d9b703e-1da7-4a85-bf5d-72aac06c548f",
      "name": "\u2705 Provisioning Report",
      "type": "n8n-nodes-base.code",
      "position": [
        1968,
        -6464
      ],
      "parameters": {
        "jsCode": "const liveResponse = $input.first().json;\nconst liveInboxes = Array.isArray(liveResponse.inboxes) ? liveResponse.inboxes : Array.isArray(liveResponse) ? liveResponse : [];\nconst liveIds = new Set(liveInboxes.map(i => String(i.inbox_id || '').toLowerCase().trim()));\n\nconst compareResult = $('\ud83d\udd0d Compare + Tier Check').first().json;\nconst wanted = [...compareResult.matched, ...compareResult.missing];\n\nconst confirmed = [];\nconst still_failed = [];\n\nfor (const inbox of wanted) {\n  if (liveIds.has(inbox.expected_inbox_id)) { confirmed.push(inbox.expected_inbox_id); }\n  else { still_failed.push(inbox.expected_inbox_id); }\n}\n\nconst allOk = still_failed.length === 0;\n\nreturn [{ json: { status: allOk ? 'PROVISIONING_COMPLETE' : 'PROVISIONING_PARTIAL', plan: compareResult.plan, wanted: wanted.length, confirmed_live: confirmed.length, failed_count: still_failed.length, confirmed, still_failed, orphaned: compareResult.orphaned || [], orphaned_count: compareResult.orphaned_count || 0, next_step: allOk ? ((compareResult.orphaned_count || 0) > 0 ? 'All wanted inboxes live. Delete orphaned inboxes then run Layer 0B.' : 'All inboxes live. Run Layer 1 to register webhooks.') : 'Some inboxes failed. Check AgentMail credentials.', timestamp: new Date().toISOString() } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "1db20e91-339f-423c-b848-3b0ea190f3f3",
      "name": "\u26d4 Tier Limit Report",
      "type": "n8n-nodes-base.code",
      "position": [
        1152,
        -6336
      ],
      "parameters": {
        "jsCode": "const data = $('\ud83d\udd0d Compare + Tier Check').first().json;\nconst plan = data.plan;\nconst missing = data.missing || [];\nconst orphaned = data.orphaned || [];\nconst orphanedIds = orphaned.map(o => o.inbox_id);\n\nlet resolution;\nif (plan.can_create_after_cleanup) {\n  resolution = { action_required: 'DELETE_ORPHANED_THEN_RETRY', instructions: ['1. Copy the orphaned inbox IDs below.', '2. Paste into \u2699\ufe0f Delete Config node.', '3. Run \u25b6\ufe0f Run Cleanup.', '4. Run \u25b6\ufe0f Run Provisioning again.'], inboxes_to_delete: orphanedIds, slots_freed: plan.freed_if_cleaned, will_succeed: plan.slots_after_cleanup >= missing.length };\n} else {\n  resolution = { action_required: 'UPGRADE_PLAN', instructions: ['Your ' + plan.tier + ' plan allows ' + plan.max_inboxes + ' inboxes.', 'You need ' + missing.length + ' new slots but only ' + plan.slots_after_cleanup + ' available after cleanup.', 'Upgrade your plan or reduce rows in Team_Config.'], will_succeed: false };\n}\n\nreturn [{ json: { status: 'TIER_BLOCKED', plan, missing: missing.map(m => m.expected_inbox_id), orphaned, resolution, timestamp: new Date().toISOString() } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "46d77930-ee2a-4b0a-8133-3e4a6db09a49",
      "name": "Note: Layer 2A Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1568,
        -6160
      ],
      "parameters": {
        "color": 7,
        "width": 1900,
        "height": 532,
        "content": "## \ud83d\udd12 LAYER 2: Hierarchy Enforcement \n**All config comes from Team_Config DataTable.**\n\n**Two webhook triggers in one workflow:**\n\n`message.sent` \u2192 Block lower-tier inboxes from that prospect\n`message.received` \u2192 Classify reply, detect signal, escalate upward\n\n**The hierarchy is driven by `priority` in Team_Config:**\n```\npriority 3 (AE)  sends \u2192 blocks priority 2 + 1\npriority 2 (BDR) sends \u2192 blocks priority 1\npriority 1 (MKT) sends \u2192 blocks nobody\n```\n\n**Escalation is driven by `escalates_to` in Team_Config:**\n```\nReply hits Marketing \u2192 escalates_to: bdr\nReply hits BDR       \u2192 escalates_to: ae\nReply hits AE        \u2192 escalates_to: (empty = top tier)\n```\n"
      },
      "typeVersion": 1
    },
    {
      "id": "5aa968fc-c155-481c-82ea-3d460032c5b4",
      "name": "\u26a1 Webhook: message.sent",
      "type": "n8n-nodes-base.webhook",
      "position": [
        2160,
        -5920
      ],
      "parameters": {
        "path": "agentmail-sent",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2
    },
    {
      "id": "6696e896-538c-4ac8-95cb-1936e9975f1a",
      "name": "\u2699\ufe0f Read Team_Config (sent)",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        2384,
        -5920
      ],
      "parameters": {
        "limit": 20,
        "orderBy": true,
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "ZPtmczWG8ZNDbAS6",
          "cachedResultUrl": "/projects/2qxSXp3XpPDzCYbp/datatables/ZPtmczWG8ZNDbAS6",
          "cachedResultName": "Team_Config"
        },
        "orderByColumn": "priority"
      },
      "typeVersion": 1.1
    },
    {
      "id": "2166def2-94ad-4413-9da6-541120f561ee",
      "name": "\ud83e\udde0 Calculate Hierarchy Blocks",
      "type": "n8n-nodes-base.code",
      "position": [
        2608,
        -5920
      ],
      "parameters": {
        "jsCode": "// ============================================\n// HIERARCHY ENFORCEMENT \u2014 Reads Team_Config\n// ============================================\n// $input = DataTable rows (config)\n// Webhook payload = named reference to webhook node\n// ============================================\n\n// 1. Read config from DataTable (comes via $input)\nconst configRows = $input.all().map(i => i.json);\n\nif (configRows.length === 0) {\n  return [{ json: { action: 'SKIP', reason: 'Team_Config DataTable is empty.' } }];\n}\n\n// 2. Read webhook payload from the WEBHOOK NODE (not $input)\nconst webhookData = $('\u26a1 Webhook: message.sent').first().json;\nconst body = webhookData.body || webhookData;\nconst event = body.event_type ? body : (body.body || body);\n\nif (!event.send || event.event_type !== 'message.sent') {\n  return [{ json: { action: 'SKIP', reason: 'Not a message.sent event. Got: ' + (event.event_type || 'unknown'), debug_keys: Object.keys(event) } }];\n}\n\n// 3. Build teams lookup from DataTable\nconst teams = {};\nfor (const row of configRows) {\n  teams[row.tier_id] = {\n    inbox: (row.inbox_address || '').toLowerCase(),\n    priority: row.priority || 0,\n    display_name: row.display_name || row.tier_id\n  };\n}\n\n// 4. Build inbox-to-tier lookup\nconst inboxToTier = {};\nfor (const [tierId, data] of Object.entries(teams)) {\n  inboxToTier[data.inbox] = tierId;\n}\n\n// 5. Parse sender and recipients\nconst send = event.send;\nconst senderInbox = (send.inbox_id || '').toLowerCase();\nconst prospects = (send.recipients || []).map(r => r.toLowerCase().trim());\n\nif (!senderInbox || prospects.length === 0) {\n  return [{ json: { action: 'SKIP', reason: 'Missing inbox_id or recipients.', event_id: event.event_id } }];\n}\n\nconst senderTierId = inboxToTier[senderInbox];\nif (!senderTierId) {\n  return [{ json: { action: 'SKIP', reason: 'Sender \"' + send.inbox_id + '\" not in Team_Config. Known inboxes: ' + Object.keys(inboxToTier).join(', '), event_id: event.event_id } }];\n}\n\nconst senderPriority = teams[senderTierId].priority;\n\n// 6. Find all tiers with lower priority\nconst lowerTiers = Object.entries(teams)\n  .filter(([tid, data]) => data.priority < senderPriority)\n  .map(([tid, data]) => ({ tier_id: tid, ...data }));\n\nif (lowerTiers.length === 0) {\n  return [{ json: { action: 'NONE', sender: send.inbox_id, sender_team: senderTierId, prospects, event_id: event.event_id, message: senderTierId.toUpperCase() + ' is lowest active tier. No blocks needed.' } }];\n}\n\n// 7. Generate block items\nconst blocks = [];\nfor (const prospect of prospects) {\n  for (const lower of lowerTiers) {\n    blocks.push({ json: {\n      action: 'BLOCK',\n      inbox_id: lower.inbox,\n      blocked_team: lower.tier_id,\n      prospect_email: prospect,\n      sender_inbox: send.inbox_id,\n      sender_team: senderTierId,\n      thread_id: send.thread_id,\n      message_id: send.message_id,\n      event_id: event.event_id,\n      reason: 'HIERARCHY_LOCK: ' + senderTierId.toUpperCase() + ' (' + send.inbox_id + ') engaged ' + prospect + '. ' + lower.tier_id.toUpperCase() + ' blocked. [event:' + event.event_id + ']'\n    } });\n  }\n}\n\nreturn blocks.length > 0 ? blocks : [{ json: { action: 'NONE', sender: send.inbox_id, sender_team: senderTierId, event_id: event.event_id, message: 'No blocks to execute.' } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "152a7142-d60b-49f3-9414-4646587d9049",
      "name": "Blocks Needed?",
      "type": "n8n-nodes-base.if",
      "position": [
        2832,
        -5920
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.action }}",
              "rightValue": "BLOCK"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ddcfd656-1c68-41c6-b8f7-c375ad1f10f8",
      "name": "\ud83d\udd12 Execute Block",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3056,
        -5984
      ],
      "parameters": {
        "url": "={{ 'https://api.agentmail.to/v0/inboxes/' + $json.inbox_id + '/lists/send/block' }}",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "jsonBody": "={\n  \"entry\": \"{{ $json.prospect_email }}\",\n  \"reason\": \"{{ $json.reason }}\"\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "4bd05047-91b0-449d-932e-0620ce005f39",
      "name": "\ud83d\udccb Enforcement Log",
      "type": "n8n-nodes-base.code",
      "position": [
        3248,
        -5984
      ],
      "parameters": {
        "jsCode": "const blocks = $input.all();\nconst calcItems = $('\ud83e\udde0 Calculate Hierarchy Blocks').all().filter(i => i.json.action === 'BLOCK');\nconst results = blocks.map((b, i) => {\n  const meta = calcItems[i] ? calcItems[i].json : {};\n  return { inbox_blocked: meta.inbox_id || 'unknown', team_blocked: meta.blocked_team || 'unknown', prospect: meta.prospect_email || 'unknown', success: b.json.scope_key || b.json.entry ? true : false };\n});\nconst first = calcItems[0] ? calcItems[0].json : {};\nreturn [{ json: { status: 'HIERARCHY_ENFORCED', trigger: 'message.sent webhook', sender: first.sender_inbox || 'unknown', sender_team: first.sender_team || 'unknown', event_id: first.event_id || 'unknown', blocks_executed: results.length, details: results, timestamp: new Date().toISOString() } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "7d4a1632-174d-47f0-a149-d2971d4ac27c",
      "name": "\ud83d\udccb No Action Log",
      "type": "n8n-nodes-base.code",
      "position": [
        3056,
        -5856
      ],
      "parameters": {
        "jsCode": "return [{ json: { status: 'NO_ACTION', trigger: 'message.sent webhook', reason: $json.reason || $json.message || 'Nothing to block.', sender: $json.sender || 'unknown', team: $json.sender_team || 'unknown', event_id: $json.event_id || 'unknown', timestamp: new Date().toISOString() } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "b2d7e135-b307-4953-bedf-ea16deeedc95",
      "name": "\u26a1 Webhook: message.received",
      "type": "n8n-nodes-base.webhook",
      "position": [
        2096,
        -5504
      ],
      "parameters": {
        "path": "agentmail-received",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2
    },
    {
      "id": "fc06c612-1612-427c-aee9-eecbc9c2962a",
      "name": "\u2699\ufe0f Read Team_Config (received)",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        2240,
        -5504
      ],
      "parameters": {
        "limit": 20,
        "orderBy": true,
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "ZPtmczWG8ZNDbAS6",
          "cachedResultUrl": "/projects/2qxSXp3XpPDzCYbp/datatables/ZPtmczWG8ZNDbAS6",
          "cachedResultName": "Team_Config"
        },
        "orderByColumn": "priority"
      },
      "typeVersion": 1.1
    },
    {
      "id": "89aad437-060e-4115-a7ed-aca6af6097cf",
      "name": "Note: Config Cleanup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2272,
        -6848
      ],
      "parameters": {
        "width": 1200,
        "height": 688,
        "content": "## \ud83d\uddd1\ufe0f CLEANUP (Config-Driven)\n\nDeletes orphaned inboxes automatically.\n\nSource of truth = Team_Config DataTable.\n\nFlow:\n1. Read Team_Config (desired inboxes)\n2. Fetch live inboxes from AgentMail\n3. Identify orphaned inboxes\n4. Delete only orphaned ones\n\n\u26a0\ufe0f Permanent deletion.\nNo manual list editing required. (good for being lazy)"
      },
      "typeVersion": 1
    },
    {
      "id": "f410506c-c53c-45b7-9a8e-ded82c72777d",
      "name": "\ud83e\udde0 Identify Orphaned",
      "type": "n8n-nodes-base.code",
      "position": [
        2576,
        -6432
      ],
      "parameters": {
        "jsCode": "// Compare DataTable vs Live\nconst configRows = $('\u2699\ufe0f Load Team_Config').all().map(i => i.json);\nconst live = $input.first().json;\nconst liveInboxes = Array.isArray(live.inboxes) ? live.inboxes : live;\n\nconst desired = new Set(configRows.map(r => (r.inbox_address || '').toLowerCase()));\n\nconst orphaned = liveInboxes\n  .map(i => (i.inbox_id || '').toLowerCase())\n  .filter(id => id && !desired.has(id));\n\nif (orphaned.length === 0) {\n  return [{ json: { action: 'NONE', message: 'No orphaned inboxes found.' } }];\n}\n\nreturn orphaned.map(id => ({\n  json: {\n    action: 'DELETE',\n    inbox_id: id,\n    url: 'https://api.agentmail.to/v0/inboxes/' + id\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "5ce70a0a-a215-4af3-9861-6bdf5ab8e57c",
      "name": "Delete Needed?",
      "type": "n8n-nodes-base.if",
      "position": [
        2800,
        -6432
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.action }}",
              "rightValue": "DELETE"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "4feab3b8-52de-4259-86dc-858ceebc6342",
      "name": "\ud83d\uddd1\ufe0f DELETE Inbox",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3024,
        -6432
      ],
      "parameters": {
        "url": "={{ $json.url }}",
        "method": "DELETE",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "f4d1a784-adbe-45d5-98f8-c0c75af89af7",
      "name": "\ud83d\udccb Cleanup Report",
      "type": "n8n-nodes-base.code",
      "position": [
        3248,
        -6432
      ],
      "parameters": {
        "jsCode": "const deleted = $input.all().map(i => i.json.inbox_id);\nreturn [{ json: {\n  status: 'CLEANUP_COMPLETE',\n  deleted_count: deleted.length,\n  deleted,\n  timestamp: new Date().toISOString()\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "b010a556-a522-43df-899b-3c9f6d85ed09",
      "name": "\ud83e\udde0 Smart Classify & Route",
      "type": "n8n-nodes-base.code",
      "position": [
        2384,
        -5504
      ],
      "parameters": {
        "jsCode": "// ============================================\n// ENHANCED ROUTING \u2014 Labels + Shielding\n// ============================================\nconst configRows = $input.all().map(i => i.json);\nconst webhookData = $('\u26a1 Webhook: message.received').first().json;\n\nconst msg = webhookData.body?.message || webhookData.message;\nif (!msg || !msg.inbox_id) return [{ json: { action: 'SKIP', reason: 'Invalid payload' } }];\n\n// 1. Identify Receiver\nconst receiver = configRows.find(c => c.inbox_address === msg.inbox_id);\nif (!receiver) return [{ json: { action: 'SKIP', reason: 'Inbox not in Team_Config' } }];\n\n// 2. Signal Check\nconst body = (msg.extracted_text || msg.text || \"\").toLowerCase();\nlet signal = 'NEUTRAL';\nif (['interested', 'call', 'pricing', 'demo', 'schedule'].some(k => body.includes(k))) signal = 'HOT';\nif (['stop', 'unsubscribe', 'remove'].some(k => body.includes(k))) signal = 'COLD';\n\n// 3. Routing Decision\nlet action = 'PROCESS'; \nlet label_to_add = `reply-${signal.toLowerCase()}`;\n// Block the receiver (e.g., Marketing) from sending more automated emails unless the lead is cold (unsubscribe)\nlet block_needed = (signal !== 'COLD' && receiver.tier_id !== 'ae');\n\nreturn [{\n  json: {\n    action,\n    signal,\n    label_to_add,\n    block_needed,\n    inbox_to_block: receiver.inbox_address,\n    prospect: msg.from,\n    thread_id: msg.thread_id,\n    message_id: msg.message_id,\n    receiving_tier: receiver.tier_id\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "db8d8bb9-7bca-4a8a-8851-f78248981daf",
      "name": "\ud83d\udd00 Route by Action",
      "type": "n8n-nodes-base.switch",
      "position": [
        2528,
        -5504
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "process",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.action }}",
                    "rightValue": "PROCESS"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "skip",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.action }}",
                    "rightValue": "SKIP"
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "ab61dbe6-37bc-4066-8bc9-5a69bd367ba7",
      "name": "\ud83c\udff7\ufe0f Apply State Label",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2672,
        -5616
      ],
      "parameters": {
        "url": "={{ 'https://api.agentmail.to/v0/inboxes/' + $json.inbox_to_block + '/messages/' + $json.message_id }}",
        "method": "PATCH",
        "options": {},
        "jsonBody": "={\n  \"add_labels\": [\"{{ $json.label_to_add }}\", \"needs-review\"],\n  \"remove_labels\": [\"unread\"]\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "52f79061-1a7b-4989-9e1c-0aee212efc81",
      "name": "Shield Needed?",
      "type": "n8n-nodes-base.if",
      "position": [
        2688,
        -5424
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "block-check",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.block_needed }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "bf408e6d-e7d8-4ebd-9bb1-0f8c5d5f544a",
      "name": "\ud83d\udd12 Execute Reply Shield",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2848,
        -5440
      ],
      "parameters": {
        "url": "={{ 'https://api.agentmail.to/v0/inboxes/' + $json.inbox_to_block + '/lists/send/block' }}",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "jsonBody": "={\n  \"entry\": \"{{ $json.prospect }}\",\n  \"reason\": \"AUTO-SHIELD: Prospect replied ({{ $json.signal }}). Halting automated sequences.\"\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "112f8ab4-58e4-4716-9918-dae94644149c",
      "name": "\ud83d\udce8 Pipeline Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1568,
        -5648
      ],
      "parameters": {
        "color": "#E8F5E9",
        "width": 1896,
        "height": 380,
        "content": "## \ud83d\udce8 Reply Shield \u2014 `message.received` Pipeline\n\n**What this does:**\nWhen a prospect replies to ANY inbox on your team, this flow:\n1. **Classifies** the reply signal (HOT / NEUTRAL / COLD)\n2. **Labels** the message in AgentMail for instant visual context\n3. **Blocks** future automated sends from that inbox to the prospect\n\n**Why it matters:**\nA prospect who says \"let's jump on a call\" should NEVER receive\na generic nurture drip 2 hours later. This flow enforces silence\nthe instant a human conversation begins.\n\n**Trigger:** AgentMail `message.received` webhook\n"
      },
      "typeVersion": 1
    },
    {
      "id": "5e50c396-f7f4-44b9-bafb-036198eca374",
      "name": "\ud83d\udd00 Router Reference",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3008,
        -5568
      ],
      "parameters": {
        "color": "#E8F5E9",
        "width": 440,
        "height": 264,
        "content": "## \ud83d\udd00 Route by Action \u2014 Switch Node\n\n**Output 0 \u2192 `PROCESS`**\nValid reply from a known inbox.\nForks to BOTH paths in parallel:\n  \u2192 \ud83c\udff7\ufe0f Apply State Label (always)\n  \u2192 Shield Needed? check (always)\n\n**Output 1 \u2192 `SKIP`**\nInvalid payload or unknown inbox.\nDead end \u2014 workflow stops.\nNo API calls, no logs, no noise."
      },
      "typeVersion": 1
    },
    {
      "id": "8079bb92-26c5-4791-a4e4-abb0acea0d7d",
      "name": "\ud83e\udde0 Planning Engine",
      "type": "n8n-nodes-base.code",
      "position": [
        848,
        -5904
      ],
      "parameters": {
        "jsCode": "const cfg = $('\ud83d\udd27 Build Config').first().json;\nconst liveWebhooks = $input.first().json.webhooks || [];\n\nconst workOrder = [];\n\nconst expectedHooks = [\n  { url: cfg.n8n_url + '/webhook/agentmail-sent', event: 'message.sent' },\n  { url: cfg.n8n_url + '/webhook/agentmail-received', event: 'message.received' }\n];\n\nfor (const hook of expectedHooks) {\n  const exists = liveWebhooks.some(w => w.url === hook.url && w.event_types && w.event_types.includes(hook.event));\n  if (!exists) {\n    workOrder.push({ action: 'CREATE_WEBHOOK', webhook_url: hook.url, event: hook.event });\n  }\n}\n\nif (workOrder.length === 0) {\n  return [{ json: { action: 'ALL_CLEAR', message: 'All webhooks registered.', config_summary: cfg, live_webhooks: liveWebhooks.map(w => ({ url: w.url, events: w.event_types })) } }];\n}\n\nreturn workOrder.map(task => ({ json: task }));"
      },
      "typeVersion": 2
    },
    {
      "id": "841e2153-82e9-4695-a2af-134083ec8022",
      "name": "GET Webhooks",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        672,
        -5904
      ],
      "parameters": {
        "url": "https://api.agentmail.to/v0/webhooks",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "32174e33-6481-4b07-87d5-304a954c4d1d",
      "name": "Route by Action",
      "type": "n8n-nodes-base.switch",
      "position": [
        1040,
        -5904
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "hook",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.action }}",
                    "rightValue": "CREATE_WEBHOOK"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "clear",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.action }}",
                    "rightValue": "ALL_CLEAR"
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "3b975b67-d823-4840-b985-ce3834503fac",
      "name": "\u2705 System Ready",
      "type": "n8n-nodes-base.code",
      "position": [
        1264,
        -5808
      ],
      "parameters": {
        "jsCode": "const cfg = $('\ud83d\udd27 Build Config').first().json;\n\nreturn [{ json: { status: 'LAYER_1_COMPLETE', message: 'All webhooks registered.', config: cfg, next_step: 'Run Layer 0B to validate full stack.', timestamp: new Date().toISOString() } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "2048db1d-6da6-4d07-a5a3-3e3e540e0c19",
      "name": "\ud83d\udce1 Register Webhook",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1264,
        -6000
      ],
      "parameters": {
        "url": "https://api.agentmail.to/v0/webhooks",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"url\": \"{{ $json.webhook_url }}\",\n  \"event_types\": [\"{{ $json.event }}\"]\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "c6ff7dfb-9833-4e30-922c-2192477cb734",
      "name": "\ud83e\uddf9 Clear Block",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2752,
        -7152
      ],
      "parameters": {
        "url": "={{ $json.url }}",
        "method": "DELETE",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "982046c5-1a71-4e66-84e9-508e837b575b",
      "name": "Weekly (Sun 00:00) - or put it to ANYTHING you like",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1616,
        -6992
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 0 * * 0"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "10118a45-134b-4548-b1bd-f8967bc314bd",
      "name": "GET blockLists",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2064,
        -6992
      ],
      "parameters": {
        "url": "={{ 'https://api.agentmail.to/v0/inboxes/' + $json.inbox_address + '/lists/send/block' }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "251e4524-8712-457e-8ad4-6a6ac3ebf52a",
      "name": "\ud83e\udde0 Find Expired Entries",
      "type": "n8n-nodes-base.code",
      "position": [
        2288,
        -6992
      ],
      "parameters": {
        "jsCode": "const responses = $input.all();\nconst configRows = $('\u2699\ufe0f Read Team_Config').all();\nconst now = new Date();\nconst expired = [];\n\nfor (let i = 0; i < responses.length; i++) {\n  const meta = configRows[i] ? configRows[i].json : {};\n  const resp = responses[i].json;\n  const entries = Array.isArray(resp.entries) ? resp.entries : Array.isArray(resp) ? resp : [];\n  const decayDays = meta.decay_days || 30;\n\n  for (const entry of entries) {\n    if (!entry.created_at) continue;\n\n    const blockDate = new Date(entry.created_at);\n    const ageDays = (now - blockDate) / (1000 * 60 * 60 * 24);\n\n    if (ageDays > decayDays) {\n      expired.push({\n        json: {\n          action: 'DELETE',\n          inbox_id: meta.inbox_address,\n          tier_id: meta.tier_id,\n          display_name: meta.display_name,\n          prospect: entry.entry,\n          age_days: Math.round(ageDays),\n          decay_threshold: decayDays,\n          url: 'https://api.agentmail.to/v0/inboxes/' + meta.inbox_address + '/lists/send/block/' + encodeURIComponent(entry.entry)\n        }\n      });\n    }\n  }\n}\n\nif (expired.length === 0) {\n  return [{ json: { action: 'NONE', message: 'No expired blocks found. All entries within decay windows.' } }];\n}\n\nreturn expired;"
      },
      "typeVersion": 2
    },
    {
      "id": "d76f0b35-ba0d-4f64-8f5e-67465b74c02c",
      "name": "Blocks to Clear?",
      "type": "n8n-nodes-base.if",
      "position": [
        2512,
        -6992
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "del",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.action }}",
              "rightValue": "DELETE"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "fd682d51-0d84-4299-9c16-ac42043db24b",
      "name": "\ud83d\udce5 Fetch Live inboxes.",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2352,
        -6432
      ],
      "parameters": {
        "url": "https://api.agentmail.to/v0/inboxes",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "28e449f2-28e5-440e-a305-f2606eae8e8a",
      "name": "Note: Database & Trigger Logic1",
      "type": "n8n-nodes-base.stickyNote",
      "disabled": true,
      "position": [
        -352,
        -6672
      ],
      "parameters": {
        "color": "#FFC107",
        "width": 512,
        "height": 352,
        "content": "## USE this as your manual TEST trigger. But make sure you run the database setup first.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "234aa99e-6311-454b-8e08-a91e9e85859c",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1344,
        -7264
      ],
      "parameters": {
        "color": 7,
        "width": 160,
        "height": 80,
        "content": "# STEP 1\n"
      },
      "typeVersion": 1
    },
    {
      "id": "3ea3f28f-9177-48a5-9773-595e402b1508",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1872,
        -6720
      ],
      "parameters": {
        "color": 7,
        "width": 160,
        "height": 80,
        "content": "# STEP 2\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "30db289e-5da5-4426-bd95-26d6547ec337",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1296,
        -6112
      ],
      "parameters": {
        "color": 7,
        "width": 160,
        "height": 80,
        "content": "# STEP 3\n"
      },
      "typeVersion": 1
    },
    {
      "id": "c5970675-04ca-47ce-a63b-c754c7d98090",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3168,
        -6720
      ],
      "parameters": {
        "color": 7,
        "width": 208,
        "height": 80,
        "content": "# OPTIONAL\n"
      },
      "typeVersion": 1
    },
    {
      "id": "c73f2318-c6e8-4bc0-8c7e-6e0f7ed308ea",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2416,
        -7296
      ],
      "parameters": {
        "color": 7,
        "width": 304,
        "height": 80,
        "content": "## Scheduled Trigger"
      },
      "typeVersion": 1
    },
    {
      "id": "b95bfba3-7469-4589-88d4-eadc7a826009",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3280,
        -5680
      ],
      "parameters": {
        "color": "#3A42E9",
        "width": 176,
        "height": 80,
        "content": "# OnEvent \n"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "GET Webhooks": {
      "main": [
        [
          {
            "node": "\ud83e\udde0 Planning Engine",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Blocks Needed?": {
      "main": [
        [
          {
            "node": "\ud83d\udd12 Execute Block",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\ud83d\udccb No Action Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Delete Needed?": {
      "main": [
        [
          {
            "node": "\ud83d\uddd1\ufe0f DELETE Inbox",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GET blockLists": {
      "main": [
        [
          {
            "node": "\ud83e\udde0 Find Expired Entries",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Shield Needed?": {
      "main": [
        [
          {
            "node": "\ud83d\udd12 Execute Reply Shield",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GET Block Lists": {
      "main": [
        [
          {
            "node": "\ud83d\udcca Compile Audit Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Action": {
      "main": [
        [
          {
            "node": "\ud83d\udce1 Register Webhook",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\u2705 System Ready",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1. Create Schema": {
      "main": [
        [
          {
            "node": "2. Generate Baseline Tiers",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Blocks to Clear?": {
      "main": [
        [
          {
            "node": "\ud83e\uddf9 Clear Block",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\ud83d\udccb All Clear",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udccb Final Fetch": {
      "main": [
        [
          {
            "node": "\u2705 Provisioning Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\uddf9 Clear Block": {
      "main": [
        [
          {
            "node": "\ud83d\udcca Decay Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd27 Build Config": {
      "main": [
        [
          {
            "node": "GET Webhooks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd12 Execute Block": {
      "main": [
        [
          {
            "node": "\ud83d\udccb Enforcement Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd00 Route Decision": {
      "main": [
        [
          {
            "node": "\ud83d\udccb Final Fetch",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\ud83d\udd00 Fan Out Missing",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\u26d4 Tier Limit Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd00 Fan Out Missing": {
      "main": [
        [
          {
            "node": "\ud83d\udcec Create Missing Inboxes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd00 Route by Action": {
      "main": [
        [
          {
            "node": "\ud83c\udff7\ufe0f Apply State Label",
            "type": "main",
            "index": 0
          },
          {
            "node": "Shield Needed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\uddd1\ufe0f DELETE Inbox": {
      "main": [
        [
          {
            "node": "\ud83d\udccb Cleanup Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\udde0 Planning Engine": {
      "main": [
        [
          {
            "node": "Route by Action",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\udde0 Identify Orphaned": {
      "main": [
        [
          {
            "node": "Delete Needed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2699\ufe0f Load Team_Config": {
      "main": [
        []
      ]
    },
    "\u2699\ufe0f Read Team_Config": {
      "main": [
        [
          {
            "node": "GET blockLists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce5 Fetch Live Inboxes": {
      "main": [
        [
          {
            "node": "\ud83d\udd0d Compare + Tier Check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce5 Fetch Live inboxes.": {
      "main": [
        [
          {
            "node": "\ud83e\udde0 Identify Orphaned",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd17 Aggregate Creations": {
      "main": [
        [
          {
            "node": "\ud83d\udccb Final Fetch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u26a1 Webhook: message.sent": {
      "main": [
        [
          {
            "node": "\u2699\ufe0f Read Team_Config (sent)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd0d Compare + Tier Check": {
      "main": [
        [
          {
            "node": "\ud83d\udd00 Route Decision",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\udde0 Find Expired Entries": {
      "main": [
        [
          {
            "node": "Blocks to Clear?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2. Generate Baseline Tiers": {
      "main": [
        [
          {
            "node": "3. Populate Database",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcec Create Missing Inboxes": {
      "main": [
        [
          {
            "node": "\ud83d\udd17 Aggregate Creations",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\udde0 Smart Classify & Route": {
      "main": [
        [
          {
            "node": "\ud83d\udd00 Route by Action",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u26a1 Webhook: message.received": {
      "main": [
        [
          {
            "node": "\u2699\ufe0f Read Team_Config (received)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2699\ufe0f Read Team_Config (sent)": {
      "main": [
        [
          {
            "node": "\ud83e\udde0 Calculate Hierarchy Blocks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\udde0 Calculate Hierarchy Blocks": {
      "main": [
        [
          {
            "node": "Blocks Needed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2699\ufe0f Read Team_Config (received)": {
      "main": [
        [
          {
            "node": "\ud83e\udde0 Smart Classify & Route",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "\u2699\ufe0f Load Team_Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Weekly (Sun 00:00) - or put it to ANYTHING you like": {
      "main": [
        [
          {
            "node": "\u2699\ufe0f Read Team_Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}