{
  "name": "Triage and route project requests with Airtable, OpenAI, Slack and Gmail",
  "nodes": [
    {
      "id": "c3cbe95a-9fa4-4f52-bc12-616a89bf98c0",
      "name": "Triage Section 4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1904,
        2752
      ],
      "parameters": {
        "color": 7,
        "width": 1900,
        "height": 800,
        "content": "## Weekly triage reporting\n\nThe reporting lane is completely separate from request intake and summarises open, blocked and unassigned work. Email delivery is optional."
      },
      "typeVersion": 1
    },
    {
      "id": "d814ec50-9496-43a4-8f53-843c2ed4d0d9",
      "name": "Triage Section 3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1904,
        1664
      ],
      "parameters": {
        "color": 7,
        "width": 2500,
        "height": 850,
        "content": "## Human approval decision\n\nA separate approval webhook looks up the pending Airtable record, applies the human decision, updates status and returns the approval outcome."
      },
      "typeVersion": 1
    },
    {
      "id": "1b213de2-3c16-4b7b-a15b-ca8c9cb16d37",
      "name": "Triage Section 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 3400,
        "height": 1450,
        "content": "## Classification, scoring and routing\n\nComplete requests are classified with OpenAI, scored with visible deterministic rules, and routed either to pending human approval or directly into delivery handoff."
      },
      "typeVersion": 1
    },
    {
      "id": "a5eaae93-3a0d-4282-8f03-5234026bb9ff",
      "name": "Triage Section 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1904,
        256
      ],
      "parameters": {
        "color": 7,
        "width": 1850,
        "height": 1200,
        "content": "## Request intake and validation\n\nWebhook and manual-test inputs converge into one normalised request. The brief-completeness check determines whether the request proceeds to triage or returns clarification questions."
      },
      "typeVersion": 1
    },
    {
      "id": "63ec9430-4519-4dcf-bcf2-db4f589f0a44",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2752,
        256
      ],
      "parameters": {
        "width": 760,
        "height": 1800,
        "content": "## Triage project requests and route approvals with Airtable and OpenAI\n\nValidates incoming briefs, returns clarification questions when information is missing, classifies complete requests with OpenAI, applies deterministic risk scoring, persists triage state in Airtable, routes higher-risk work to a human approval endpoint, and creates a structured delivery handoff.\n\nSETUP\n• Create one Airtable table with these text fields (Triage_Score as a number): Request_ID, Request_Title, Request_Type, Requester, Requester_Email, Objective, Deliverables, Due_Date, Created_At, Status, Triage_Route, Triage_Score, Triage_Summary, Risk_Level, Recommended_Owner, Next_Action, Approval_Status, Assigned_Owner, Approver_Name.\n• Add Airtable base/table IDs in each config node.\n• Select Airtable and OpenAI credentials.\n• Optional Slack/email nodes are disabled by default.\n• Test manual intake, webhook intake, clarification, approval, rejection and weekly reporting.\n\nCONTROLS\n• Manual runs never enter Respond to Webhook nodes.\n• Weekly reporting is isolated from request intake.\n• High-risk requests remain pending until a separate human approval event is received."
      },
      "typeVersion": 1
    },
    {
      "id": "96872b6e-0849-4eb8-b993-e1b3ffc32ce8",
      "name": "When Project Request Received",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -1744,
        608
      ],
      "parameters": {
        "path": "project-request-triage",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "cd4a5ff4-6208-42d2-9b1f-f92467a5a696",
      "name": "Manual Execution Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -1744,
        880
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "57304d16-868a-448e-87dd-ac9b70177127",
      "name": "Set Example Request Data",
      "type": "n8n-nodes-base.set",
      "position": [
        -1424,
        880
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "1ca835d4-8553-4880-af8a-77b11434f8f8",
              "name": "request_id",
              "type": "string",
              "value": "REQ-EXAMPLE-001"
            },
            {
              "id": "90a1810d-56fa-4a05-9e34-3271e1ee8868",
              "name": "requester_name",
              "type": "string",
              "value": "Alex Morgan"
            },
            {
              "id": "13d57be3-b89c-4905-ac62-9cf707afea94",
              "name": "requester_email",
              "type": "string",
              "value": "user@example.com"
            },
            {
              "id": "761570f5-bd38-411a-936f-cf4695ba429c",
              "name": "request_title",
              "type": "string",
              "value": "Automate weekly customer reporting"
            },
            {
              "id": "042c03ad-fbbd-4a47-829a-57d38b0f17b7",
              "name": "objective",
              "type": "string",
              "value": "Reduce the time spent rebuilding weekly reports and improve visibility for account teams."
            },
            {
              "id": "9ea3fd97-6813-4566-a8dc-0b6eca5d4275",
              "name": "deliverables",
              "type": "string",
              "value": "Automated weekly summary, exception report and approval step before client distribution."
            },
            {
              "id": "49065fea-95d9-43e9-acec-04e50377f2b1",
              "name": "due_date",
              "type": "string",
              "value": "2026-09-30"
            },
            {
              "id": "a44268dc-d97b-4bbd-902e-465a73ead287",
              "name": "budget_status",
              "type": "string",
              "value": "Unknown"
            },
            {
              "id": "7465856e-7300-4560-bcde-3c43fa43dac4",
              "name": "required_systems",
              "type": "string",
              "value": "CRM, spreadsheet tracker, email"
            },
            {
              "id": "0c8ce951-6e50-4b6e-aeed-b154392a75a5",
              "name": "approvals_required",
              "type": "string",
              "value": "Operations lead and commercial owner"
            },
            {
              "id": "946e79d5-d850-4dfe-9f00-f7ce04e56954",
              "name": "business_impact",
              "type": "string",
              "value": "Medium"
            },
            {
              "id": "fb214848-a29a-43eb-98d7-3d5d29e35068",
              "name": "urgency",
              "type": "string",
              "value": "Medium"
            },
            {
              "id": "5b96f7d2-1e75-45bb-ae9e-5605aa22a0fa",
              "name": "data_sensitivity",
              "type": "string",
              "value": "Internal"
            },
            {
              "id": "507e3a7d-a917-48bc-be77-9abe5f7b8ba2",
              "name": "request_type_hint",
              "type": "string",
              "value": "Automation"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "c4d66568-6fb7-4394-afdb-6834d9dc2224",
      "name": "Set Request Triage Config",
      "type": "n8n-nodes-base.set",
      "position": [
        -1424,
        608
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "e4f2d3b7-73c7-4ae2-8086-07fb8d84de82",
              "name": "airtable_base_id",
              "type": "string",
              "value": "YOUR_AIRTABLE_BASE_ID"
            },
            {
              "id": "4bd8df19-2d02-418c-89f0-98ec27dc8824",
              "name": "airtable_table_id",
              "type": "string",
              "value": "YOUR_AIRTABLE_TABLE_ID"
            },
            {
              "id": "ce7822f2-74c9-423c-b23f-359e4c56e165",
              "name": "notification_email",
              "type": "string",
              "value": "YOUR_TRIAGE_NOTIFICATION_EMAIL"
            },
            {
              "id": "7dea6555-a8ca-469c-83ce-4e4022cf229c",
              "name": "slack_channel_id",
              "type": "string",
              "value": "YOUR_SLACK_CHANNEL_ID"
            },
            {
              "id": "7cf24cd9-10df-415d-ae38-b7913302cadd",
              "name": "high_risk_threshold",
              "type": "number",
              "value": 70
            },
            {
              "id": "7a1baed4-a0ac-463b-b4fd-7cec6122f245",
              "name": "approval_required_threshold",
              "type": "number",
              "value": 45
            },
            {
              "id": "18419e9a-ed92-49c6-80c5-d1a7d0038e1a",
              "name": "max_days_without_owner",
              "type": "number",
              "value": 2
            },
            {
              "id": "f3f0e97c-2f83-41f1-bfe7-89b42d711536",
              "name": "weekly_summary_day",
              "type": "string",
              "value": "Friday"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "3746b104-9fa6-47db-bee1-136cbe4c8ed9",
      "name": "Normalize Project Request",
      "type": "n8n-nodes-base.code",
      "position": [
        -1088,
        720
      ],
      "parameters": {
        "jsCode": "const input = $input.first()?.json || {};\n\nconst request = input.body && typeof input.body === 'object'\n  ? input.body\n  : input;\n\nconst clean = (v) => String(v ?? '').trim();\n\nreturn [{\n  json: {\n    request_id: clean(request.request_id) || `REQ-${Date.now()}`,\n    requester_name: clean(request.requester_name),\n    requester_email: clean(request.requester_email),\n    request_title: clean(request.request_title),\n    objective: clean(request.objective),\n    deliverables: clean(request.deliverables),\n    due_date: clean(request.due_date),\n    budget_status: clean(request.budget_status),\n    required_systems: clean(request.required_systems),\n    approvals_required: clean(request.approvals_required),\n    business_impact: clean(request.business_impact),\n    urgency: clean(request.urgency),\n    data_sensitivity: clean(request.data_sensitivity),\n    request_type_hint: clean(request.request_type_hint),\n    request_source: input.body ? 'webhook' : 'manual',\n    received_at: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "d8c88c1d-cf38-4ed5-9959-829744b576ef",
      "name": "Check Brief Completeness",
      "type": "n8n-nodes-base.code",
      "position": [
        -752,
        720
      ],
      "parameters": {
        "jsCode": "const j = $input.first().json || {};\n\nconst required = [\n  'requester_name',\n  'requester_email',\n  'request_title',\n  'objective',\n  'deliverables',\n  'due_date'\n];\n\nconst missing = required.filter(k => !String(j[k] || '').trim());\n\nconst questions = [];\nif (!j.objective) questions.push('What business outcome should this request achieve?');\nif (!j.deliverables) questions.push('What specific outputs or deliverables are required?');\nif (!j.due_date) questions.push('What is the required delivery date?');\nif (!j.required_systems) questions.push('Which systems, tools or data sources are involved?');\nif (!j.approvals_required) questions.push('Who needs to approve the work or final output?');\nif (!j.budget_status) questions.push('Is budget or commercial approval already confirmed?');\n\nreturn [{\n  json: {\n    ...j,\n    brief_complete: missing.length === 0,\n    missing_required_fields: missing,\n    clarification_questions: questions\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "bb4a40e4-9375-4092-9fba-42b6c932f568",
      "name": "If Brief Complete",
      "type": "n8n-nodes-base.if",
      "position": [
        -416,
        720
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "85eea233-849b-4247-9f90-231a420d38a4",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.brief_complete }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "cfe3dc87-3a25-466e-b0b0-882d16a8625a",
      "name": "Prepare Clarification",
      "type": "n8n-nodes-base.code",
      "position": [
        -64,
        1072
      ],
      "parameters": {
        "jsCode": "const j = $input.first().json || {};\nconst questions = Array.isArray(j.clarification_questions) ? j.clarification_questions : [];\n\nreturn [{\n  json: {\n    ...j,\n    triage_status: 'Information Required',\n    next_action: 'Requester to provide missing information',\n    action_owner: j.requester_name || 'Requester',\n    clarification_message:\n      `Thanks for the request. Before triage can continue, please confirm:\\n\\n` +\n      questions.map((q, i) => `${i + 1}. ${q}`).join('\\n')\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "9c5a42de-a980-4469-b425-baf15daef16b",
      "name": "OpenAI Classify Request",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -64,
        560
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-5-mini",
          "cachedResultName": "GPT-5 mini"
        },
        "options": {},
        "responses": {
          "values": [
            {
              "role": "system",
              "content": "You are a project triage assistant. Classify the request using only the supplied evidence.\n\nReturn valid JSON with:\nrequest_type: one of Automation, Content Production, Data/Reporting, Integration, AI/Agent, Consulting, Other\ndelivery_route: short plain-English route\nsummary: max 80 words\nrisks: array of concise risks\ndependencies: array\ndecision_needed: array\nrecommended_owner_type: short role label\ncomplexity: Low, Medium or High\n\nDo not invent budget, deadlines, systems, approvals or owners."
            },
            {
              "content": "=Request title: {{$json.request_title}}\nObjective: {{$json.objective}}\nDeliverables: {{$json.deliverables}}\nDue date: {{$json.due_date}}\nBudget status: {{$json.budget_status}}\nSystems: {{$json.required_systems}}\nApprovals: {{$json.approvals_required}}\nBusiness impact: {{$json.business_impact}}\nUrgency: {{$json.urgency}}\nData sensitivity: {{$json.data_sensitivity}}\nRequest type hint: {{$json.request_type_hint}}\n\nReturn only valid JSON."
            }
          ]
        },
        "builtInTools": {}
      },
      "typeVersion": 2.3
    },
    {
      "id": "49a5951f-b82f-43bb-b8e3-28e62a54ee23",
      "name": "Parse Classification Result",
      "type": "n8n-nodes-base.code",
      "position": [
        272,
        560
      ],
      "parameters": {
        "jsCode": "const input = $input.first().json || {};\nconst raw =\n  input.output?.[0]?.content?.find(c => c.type === 'output_text')?.text ||\n  input.output?.[0]?.content?.[0]?.text ||\n  input.text ||\n  '';\n\nif (!raw) throw new Error('No classification output returned.');\n\nconst cleaned = String(raw).replace(/```json/gi,'').replace(/```/g,'').trim();\nlet parsed;\ntry { parsed = JSON.parse(cleaned); }\ncatch { throw new Error('Classification output was not valid JSON.'); }\n\nconst source = $('Check Brief Completeness').first().json;\n\nreturn [{\n  json: {\n    ...source,\n    request_type: parsed.request_type || 'Other',\n    delivery_route: parsed.delivery_route || '',\n    triage_summary: parsed.summary || '',\n    ai_risks: Array.isArray(parsed.risks) ? parsed.risks : [],\n    dependencies: Array.isArray(parsed.dependencies) ? parsed.dependencies : [],\n    decisions_needed: Array.isArray(parsed.decision_needed) ? parsed.decision_needed : [],\n    recommended_owner_type: parsed.recommended_owner_type || 'Delivery Lead',\n    complexity: parsed.complexity || 'Medium'\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "0986c12e-9d0c-41dd-8014-4f0e2e7d9b6b",
      "name": "Calculate Risk and Urgency",
      "type": "n8n-nodes-base.code",
      "position": [
        624,
        560
      ],
      "parameters": {
        "jsCode": "const j = $input.first().json || {};\n\nlet score = 0;\nconst reasons = [];\n\nconst urgency = String(j.urgency || '').toLowerCase();\nif (urgency === 'high') { score += 20; reasons.push('High urgency'); }\nelse if (urgency === 'medium') score += 10;\n\nconst impact = String(j.business_impact || '').toLowerCase();\nif (impact === 'high') { score += 20; reasons.push('High business impact'); }\nelse if (impact === 'medium') score += 10;\n\nconst sensitivity = String(j.data_sensitivity || '').toLowerCase();\nif (sensitivity.includes('sensitive') || sensitivity.includes('confidential')) {\n  score += 20; reasons.push('Sensitive data involved');\n} else if (sensitivity.includes('internal')) {\n  score += 5;\n}\n\nif (String(j.budget_status || '').toLowerCase().includes('unknown')) {\n  score += 10; reasons.push('Budget not confirmed');\n}\n\nif ((j.ai_risks || []).length >= 3) {\n  score += 10; reasons.push('Multiple delivery risks identified');\n}\n\nconst complexity = String(j.complexity || '').toLowerCase();\nif (complexity === 'high') { score += 15; reasons.push('High complexity'); }\nelse if (complexity === 'medium') score += 5;\n\nif (String(j.approvals_required || '').trim()) {\n  score += 5;\n}\n\nscore = Math.min(score, 100);\n\nconst cfg = $('Set Request Triage Config').first().json || {};\nconst high = Number(cfg.high_risk_threshold || 70);\nconst approval = Number(cfg.approval_required_threshold || 45);\n\nlet route;\nif (score >= high) route = 'High Risk Review';\nelse if (score >= approval) route = 'Approval Required';\nelse route = 'Standard Triage';\n\nreturn [{\n  json: {\n    ...j,\n    triage_score: score,\n    triage_risk_level: score >= high ? 'High' : score >= approval ? 'Medium' : 'Low',\n    triage_route: route,\n    score_reasons: reasons\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "06431592-1f91-458d-8ead-ff6a45bcc311",
      "name": "Store Triage Record in Airtable",
      "type": "n8n-nodes-base.airtable",
      "position": [
        1296,
        368
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Set Request Triage Config').first().json.airtable_base_id }}"
        },
        "table": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Set Request Triage Config').first().json.airtable_table_id }}"
        },
        "columns": {
          "value": {
            "Status": "={{ $json.triage_score >= Number($('Set Request Triage Config').first().json.approval_required_threshold || 45) ? 'Approval Required' : 'In Triaging' }}",
            "Due_Date": "={{ $json.due_date }}",
            "Objective": "={{ $json.objective }}",
            "Requester": "={{ $json.requester_name }}",
            "Created_At": "={{ $json.received_at }}",
            "Request_ID": "={{ $json.request_id }}",
            "Risk_Level": "={{ $json.triage_risk_level }}",
            "Next_Action": "={{ $json.triage_score >= Number($('Set Request Triage Config').first().json.approval_required_threshold || 45) ? 'Await human approval' : 'Assign owner and confirm delivery handoff' }}",
            "Deliverables": "={{ $json.deliverables }}",
            "Request_Type": "={{ $json.request_type }}",
            "Triage_Route": "={{ $json.triage_route }}",
            "Triage_Score": "={{ $json.triage_score }}",
            "Request_Title": "={{ $json.request_title }}",
            "Triage_Summary": "={{ $json.triage_summary }}",
            "Approval_Status": "={{ $json.triage_score >= Number($('Set Request Triage Config').first().json.approval_required_threshold || 45) ? 'Pending' : 'Not Required' }}",
            "Requester_Email": "={{ $json.requester_email }}",
            "Recommended_Owner": "={{ $json.recommended_owner_type || 'Delivery Lead' }}"
          },
          "schema": [],
          "mappingMode": "defineBelow",
          "matchingColumns": []
        },
        "options": {},
        "operation": "create"
      },
      "typeVersion": 2.1
    },
    {
      "id": "1f17a72e-049a-4cfa-a635-62d69be2a721",
      "name": "If Needs Approval",
      "type": "n8n-nodes-base.if",
      "position": [
        960,
        560
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "41cd535d-f07b-41fa-b727-a87570657826",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.triage_score >= Number($('Set Request Triage Config').first().json.approval_required_threshold || 45) }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "c6a821b9-b4ff-43dd-a123-a9c6c6ba2e62",
      "name": "Prepare Approval Request Data",
      "type": "n8n-nodes-base.code",
      "position": [
        1632,
        368
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "let ctx = {};\ntry { ctx = $('Calculate Risk and Urgency').item.json || {}; } catch {}\nconst rec = $input.item.json || {};\nconst f = rec.fields || {};\nif (!ctx.request_id) {\n  // Fallback: rebuild context from the Airtable record that was just created.\n  ctx = { request_id: f.Request_ID, request_title: f.Request_Title, request_type: f.Request_Type,\n    requester_name: f.Requester, requester_email: f.Requester_Email, objective: f.Objective,\n    deliverables: f.Deliverables, due_date: f.Due_Date, triage_route: f.Triage_Route,\n    triage_score: f.Triage_Score, triage_summary: f.Triage_Summary, triage_risk_level: f.Risk_Level,\n    recommended_owner_type: f.Recommended_Owner };\n}\nconst j = ctx;\nreturn { json: {\n  ...j,\n  airtable_record_id: rec.id || '',\n  triage_status: 'Approval Required',\n  approval_status: 'Pending',\n  next_action: 'Await human approval',\n  approval_reason: `Triage score ${j.triage_score}/100 (${j.triage_risk_level}). Route: ${j.triage_route}.`,\n  approval_message:\n    `Approval required for ${j.request_title}\\n\\n` +\n    `Request ID: ${j.request_id}\\n` +\n    `Summary: ${j.triage_summary}\\n` +\n    `Risk: ${j.triage_risk_level} (${j.triage_score}/100)\\n` +\n    `Due: ${j.due_date}\\n` +\n    `Budget: ${j.budget_status}\\n` +\n    `Recommended owner: ${j.recommended_owner_type}\\n\\n` +\n    `POST the decision to /webhook/project-request-approval with request_id, decision and approver_name.`\n}};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "42c3267a-70f9-414c-a59c-bef67ee749a8",
      "name": "Post Approval to Slack",
      "type": "n8n-nodes-base.slack",
      "disabled": true,
      "position": [
        1984,
        272
      ],
      "parameters": {
        "text": "={{ $json.approval_message }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Set Request Triage Config').first().json.slack_channel_id }}"
        },
        "otherOptions": {}
      },
      "typeVersion": 2.2
    },
    {
      "id": "cf5e4596-06a2-49cf-8554-06db880cc5fb",
      "name": "Log Approval Decision",
      "type": "n8n-nodes-base.set",
      "disabled": true,
      "position": [
        1808,
        560
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "2fdf8738-fda9-44e3-8219-da1c93071dc7",
              "name": "approval_decision",
              "type": "string",
              "value": "APPROVED"
            },
            {
              "id": "6359fb0b-11ea-41a0-a7a2-12ea4976fd93",
              "name": "approver_name",
              "type": "string",
              "value": "YOUR_APPROVER"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f6cbb8b9-ad65-432f-8121-590c567e1e32",
      "name": "If Request Approved",
      "type": "n8n-nodes-base.if",
      "disabled": true,
      "position": [
        2112,
        560
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "8165bae9-86b6-49d6-865d-94e2014d14e0",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ String($json.approval_decision || '').toUpperCase() }}",
              "rightValue": "APPROVED"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "21895144-104b-441e-8a88-e8b6ba9c749e",
      "name": "Assign Triage Owner",
      "type": "n8n-nodes-base.code",
      "position": [
        1632,
        752
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "let ctx = {};\ntry { ctx = $('Calculate Risk and Urgency').item.json || {}; } catch {}\nconst rec = $input.item.json || {};\nconst f = rec.fields || {};\nif (!ctx.request_id) {\n  // Fallback: rebuild context from the Airtable record that was just created.\n  ctx = { request_id: f.Request_ID, request_title: f.Request_Title, request_type: f.Request_Type,\n    requester_name: f.Requester, requester_email: f.Requester_Email, objective: f.Objective,\n    deliverables: f.Deliverables, due_date: f.Due_Date, triage_route: f.Triage_Route,\n    triage_score: f.Triage_Score, triage_summary: f.Triage_Summary, triage_risk_level: f.Risk_Level,\n    recommended_owner_type: f.Recommended_Owner };\n}\n\nreturn { json: {\n  ...ctx,\n  airtable_record_id: rec.id || '',\n  assigned_owner: ctx.recommended_owner_type || 'Delivery Lead',\n  triage_status: 'Assigned / In Progress',\n  next_action: 'Schedule delivery kick-off and confirm scope, dependencies and milestones'\n}};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "07b22b01-6bbf-4842-91b9-c5b6a2ee515c",
      "name": "Prepare Handoff",
      "type": "n8n-nodes-base.code",
      "position": [
        1984,
        752
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const j = $input.item.json || {};\nreturn {\n  json: {\n    ...j,\n    handoff_summary: {\n      request_id: j.request_id,\n      title: j.request_title,\n      objective: j.objective,\n      deliverables: j.deliverables,\n      due_date: j.due_date,\n      request_type: j.request_type,\n      delivery_route: j.delivery_route,\n      owner: j.assigned_owner,\n      dependencies: j.dependencies || [],\n      decisions_needed: j.decisions_needed || [],\n      risks: j.ai_risks || [],\n      next_action: j.next_action\n    }\n  }\n};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "079c9c16-f043-4142-b974-1ecb633fa46c",
      "name": "Patch Airtable Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2320,
        752
      ],
      "parameters": {
        "url": "={{ 'https://api.airtable.com/v0/' + $('Set Request Triage Config').first().json.airtable_base_id + '/' + $('Set Request Triage Config').first().json.airtable_table_id }}",
        "body": "={{ JSON.stringify({\n  records: [{\n    id: $json.id || $json.airtable_record_id || '',\n    fields: {\n      Status: $json.triage_status || 'Assigned / In Progress',\n      Assigned_Owner: $json.assigned_owner || '',\n      Next_Action: $json.next_action || '',\n      Approval_Status: $json.approval_decision || 'Not Required'\n    }\n  }]\n}) }}",
        "method": "PATCH",
        "options": {},
        "sendBody": true,
        "contentType": "raw",
        "authentication": "predefinedCredentialType",
        "rawContentType": "application/json",
        "nodeCredentialType": "airtableTokenApi"
      },
      "typeVersion": 4.4
    },
    {
      "id": "191aea5d-eef7-44e7-8807-3d2ff5e0bf90",
      "name": "Send Triage Outcome",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2992,
        672
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ {\n  request_id: $json.request_id,\n  status: $json.triage_status || 'In Triaging',\n  triage_score: $json.triage_score,\n  risk_level: $json.triage_risk_level,\n  route: $json.triage_route,\n  summary: $json.triage_summary,\n  assigned_owner: $json.assigned_owner || '',\n  next_action: $json.next_action || '',\n  handoff: $json.handoff_summary || {}\n} }}"
      },
      "typeVersion": 1.4
    },
    {
      "id": "3a8b1db5-04d5-407c-b94c-f9894c1fd449",
      "name": "Prepare Rejection Response",
      "type": "n8n-nodes-base.code",
      "disabled": true,
      "position": [
        2560,
        400
      ],
      "parameters": {
        "jsCode": "const j = $input.first().json || {};\nreturn [{\n  json: {\n    ...j,\n    triage_status: 'Closed / Declined',\n    next_action: 'Review rejection reason before resubmitting',\n    rejection_reason: 'Approval was not granted.'\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "29ce50d1-8206-494e-8369-672b6b4b63dd",
      "name": "Send Clarification Needed Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        624,
        992
      ],
      "parameters": {
        "options": {
          "responseCode": 400
        },
        "respondWith": "json",
        "responseBody": "={{ {\n  request_id: $json.request_id,\n  status: $json.triage_status,\n  missing_fields: $json.missing_required_fields,\n  questions: $json.clarification_questions,\n  message: $json.clarification_message\n} }}"
      },
      "typeVersion": 1.4
    },
    {
      "id": "d9e1339b-cf03-487e-8fdf-71b7a874dab5",
      "name": "Weekly Triage Summary Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1744,
        3056
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                5
              ],
              "triggerAtHour": 15
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "24507e03-73db-4b3b-8b70-482c66a70d68",
      "name": "Fetch Open Triage Requests",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1088,
        3056
      ],
      "parameters": {
        "url": "={{ 'https://api.airtable.com/v0/' + $('Set Weekly Reporting Config').first().json.airtable_base_id + '/' + $('Set Weekly Reporting Config').first().json.airtable_table_id }}",
        "options": {},
        "sendQuery": true,
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "filterByFormula",
              "value": "NOT({Status}='Closed / Declined')"
            },
            {
              "name": "maxRecords",
              "value": "100"
            }
          ]
        },
        "nodeCredentialType": "airtableTokenApi"
      },
      "typeVersion": 4.4
    },
    {
      "id": "453ff59d-4b0a-4e9b-a201-9995e1fa71da",
      "name": "Compile Weekly Summary",
      "type": "n8n-nodes-base.code",
      "position": [
        -752,
        3056
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json || {};\nconst records = Array.isArray(data.records) ? data.records : [];\n\nconst rows = records.map(r => ({ id: r.id, ...(r.fields || {}) }));\n\nconst counts = rows.reduce((acc, r) => {\n  const status = String(r.Status || 'Unknown');\n  acc[status] = (acc[status] || 0) + 1;\n  return acc;\n}, {});\n\nconst blocked = rows.filter(r =>\n  /information required|on hold|approval/i.test(String(r.Status || ''))\n);\n\nconst noOwner = rows.filter(r => !String(r.Assigned_Owner || '').trim());\n\nreturn [{\n  json: {\n    total_open: rows.length,\n    status_counts: counts,\n    blocked_count: blocked.length,\n    unassigned_count: noOwner.length,\n    blocked_requests: blocked.slice(0, 10),\n    unassigned_requests: noOwner.slice(0, 10),\n    generated_at: new Date().toISOString(),\n    markdown_summary: [\n      '# Weekly Project Triage Summary',\n      '',\n      `Open requests: ${rows.length}`,\n      `Blocked / awaiting action: ${blocked.length}`,\n      `Unassigned: ${noOwner.length}`,\n      '',\n      '## Status counts',\n      ...Object.entries(counts).map(([k,v]) => `- ${k}: ${v}`),\n      '',\n      '## Blocked / awaiting action',\n      ...(blocked.length ? blocked.slice(0,10).map(r => `- ${r.Request_ID || ''} — ${r.Request_Title || 'Untitled'} — ${r.Status || ''}`) : ['- None'])\n    ].join('\\n')\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "95f1a6fd-f5ab-4a11-96ee-92757d3b3251",
      "name": "Email Weekly Summary",
      "type": "n8n-nodes-base.gmail",
      "disabled": true,
      "position": [
        -416,
        3056
      ],
      "parameters": {
        "sendTo": "={{ $('Set Weekly Reporting Config').first().json.notification_email }}",
        "message": "={{ $json.markdown_summary }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "Weekly project triage summary"
      },
      "typeVersion": 2.1
    },
    {
      "id": "385ddb09-e797-4021-9692-12da57482f3b",
      "name": "Set Weekly Reporting Config",
      "type": "n8n-nodes-base.set",
      "position": [
        -1424,
        3056
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "fa3e489c-f0d2-4a4f-9337-4e1487739c41",
              "name": "airtable_base_id",
              "type": "string",
              "value": "YOUR_AIRTABLE_BASE_ID"
            },
            {
              "id": "a1bc45a4-0340-4418-85b7-7bf094ea707f",
              "name": "airtable_table_id",
              "type": "string",
              "value": "YOUR_AIRTABLE_TABLE_ID"
            },
            {
              "id": "51fbdcbb-2d42-493d-8a2e-78fad053e503",
              "name": "notification_email",
              "type": "string",
              "value": "YOUR_TRIAGE_NOTIFICATION_EMAIL"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "1909fa48-056a-4827-8a33-3928e5f4fce5",
      "name": "Manual Triage Complete",
      "type": "n8n-nodes-base.noOp",
      "position": [
        2992,
        832
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "df20aff0-ef91-4866-9e0a-dea282008620",
      "name": "Manual Clarification Complete",
      "type": "n8n-nodes-base.noOp",
      "position": [
        624,
        1152
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "f31e4f8d-3859-4677-87b7-4d3c4aad345c",
      "name": "If Request Came From Webhook",
      "type": "n8n-nodes-base.if",
      "position": [
        2656,
        752
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "6300ac9c-5316-4707-8f4b-1e7821d044a6",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.request_source }}",
              "rightValue": "webhook"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "e5a8e7d6-5ed1-42b3-bbfe-8ae91992483a",
      "name": "If Clarification Request Is Webhook",
      "type": "n8n-nodes-base.if",
      "position": [
        272,
        1072
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "ccb5165c-8db3-4201-a16a-b7e795561ce7",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.request_source }}",
              "rightValue": "webhook"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "f3726654-52f6-477f-a3de-28b422339599",
      "name": "When Approval Decision Received",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -1744,
        1952
      ],
      "parameters": {
        "path": "project-request-approval",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "428abf1d-3d6e-4a99-90e6-af9eca1e8feb",
      "name": "Set Approval Config",
      "type": "n8n-nodes-base.set",
      "position": [
        -1424,
        1952
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "c6b27218-e3f0-4d1e-ba6b-178a01441d89",
              "name": "airtable_base_id",
              "type": "string",
              "value": "YOUR_AIRTABLE_BASE_ID"
            },
            {
              "id": "087d4969-d61e-4cab-8c41-c5cdf7580c24",
              "name": "airtable_table_id",
              "type": "string",
              "value": "YOUR_AIRTABLE_TABLE_ID"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "f5bc0c45-f8fb-4e9a-a0d5-7ef8965d69f9",
      "name": "Normalise Approval Decision",
      "type": "n8n-nodes-base.code",
      "position": [
        -1088,
        1952
      ],
      "parameters": {
        "jsCode": "const input = $input.first()?.json || {};\nconst body = input.body && typeof input.body === 'object' ? input.body : input;\nconst request_id = String(body.request_id || '').trim();\nconst decision = String(body.decision || '').trim().toUpperCase();\nconst approver_name = String(body.approver_name || '').trim();\n\nif (!request_id) throw new Error('request_id is required.');\nif (!['APPROVED','REJECTED','CHANGES_REQUESTED'].includes(decision)) {\n  throw new Error('decision must be APPROVED, REJECTED or CHANGES_REQUESTED.');\n}\n\nreturn [{ json: {\n  request_id,\n  approval_decision: decision,\n  approver_name,\n  approval_received_at: new Date().toISOString()\n}}];"
      },
      "typeVersion": 2
    },
    {
      "id": "79c4e2f4-265e-4095-916b-307eb953b0e5",
      "name": "Find Pending Airtable Request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -752,
        1952
      ],
      "parameters": {
        "url": "={{ 'https://api.airtable.com/v0/' + $('Set Approval Config').first().json.airtable_base_id + '/' + $('Set Approval Config').first().json.airtable_table_id }}",
        "options": {},
        "sendQuery": true,
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "filterByFormula",
              "value": "={{ \"{Request_ID}='\" + $json.request_id + \"'\" }}"
            },
            {
              "name": "maxRecords",
              "value": "1"
            }
          ]
        },
        "nodeCredentialType": "airtableTokenApi"
      },
      "typeVersion": 4.4
    },
    {
      "id": "386bc9ec-bceb-4603-87d7-e9dafe2ff0f4",
      "name": "Prepare Approval Update",
      "type": "n8n-nodes-base.code",
      "position": [
        -416,
        1952
      ],
      "parameters": {
        "jsCode": "const lookup = $input.first()?.json || {};\nconst decision = $('Normalise Approval Decision').first().json || {};\nconst record = Array.isArray(lookup.records) ? lookup.records[0] : null;\nif (!record?.id) throw new Error('No Airtable triage record found for request_id.');\n\nconst existing = record.fields || {};\nconst approved = decision.approval_decision === 'APPROVED';\nconst rejected = decision.approval_decision === 'REJECTED';\n\nlet status = 'Approval Required';\nlet nextAction = 'Reviewer requested changes before approval';\nif (approved) {\n  status = 'Assigned / In Progress';\n  nextAction = 'Schedule delivery kick-off and confirm scope, dependencies and milestones';\n} else if (rejected) {\n  status = 'Closed / Declined';\n  nextAction = 'Review rejection reason before resubmitting';\n}\n\nreturn [{ json: {\n  ...existing,\n  airtable_record_id: record.id,\n  request_id: decision.request_id,\n  approval_decision: decision.approval_decision,\n  approver_name: decision.approver_name,\n  approval_received_at: decision.approval_received_at,\n  triage_status: status,\n  next_action: nextAction,\n  assigned_owner: approved ? (existing.Assigned_Owner || existing.Recommended_Owner || 'Delivery Lead') : ''\n}}];"
      },
      "typeVersion": 2
    },
    {
      "id": "35ff2368-8586-4b04-8bce-44f63979d080",
      "name": "Patch Airtable Approval Result",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -64,
        1952
      ],
      "parameters": {
        "url": "={{ 'https://api.airtable.com/v0/' + $('Set Approval Config').first().json.airtable_base_id + '/' + $('Set Approval Config').first().json.airtable_table_id + '/' + $json.airtable_record_id }}",
        "body": "={{ JSON.stringify({\n  fields: {\n    Status: $json.triage_status,\n    Assigned_Owner: $json.assigned_owner || '',\n    Next_Action: $json.next_action || '',\n    Approval_Status: $json.approval_decision,\n    Approver_Name: $json.approver_name || ''\n  }\n}) }}",
        "method": "PATCH",
        "options": {},
        "sendBody": true,
        "contentType": "raw",
        "authentication": "predefinedCredentialType",
        "rawContentType": "application/json",
        "nodeCredentialType": "airtableTokenApi"
      },
      "typeVersion": 4.4
    },
    {
      "id": "63e320f5-07ac-4016-bad0-4b25f97e79d6",
      "name": "Send Approval Outcome",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        272,
        1952
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ {\n  request_id: $('Prepare Approval Update').first().json.request_id,\n  decision: $('Prepare Approval Update').first().json.approval_decision,\n  status: $('Prepare Approval Update').first().json.triage_status,\n  assigned_owner: $('Prepare Approval Update').first().json.assigned_owner || '',\n  next_action: $('Prepare Approval Update').first().json.next_action\n} }}"
      },
      "typeVersion": 1.4
    },
    {
      "id": "34fed183-3bb1-4562-aa44-a5057c9d9944",
      "name": "If Pending Approval Request Is Webhook",
      "type": "n8n-nodes-base.if",
      "position": [
        1984,
        464
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "0d2ea28b-9425-42eb-9907-46c6ffad6b8e",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.request_source }}",
              "rightValue": "webhook"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "6c7789e0-60ec-4781-9d77-6e08deb8e4ae",
      "name": "Send Pending Approval Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2320,
        384
      ],
      "parameters": {
        "options": {
          "responseCode": 202
        },
        "respondWith": "json",
        "responseBody": "={{ {\n  request_id: $json.request_id,\n  status: 'Approval Required',\n  approval_status: 'Pending',\n  triage_score: $json.triage_score,\n  risk_level: $json.triage_risk_level,\n  route: $json.triage_route,\n  next_action: 'Await human approval'\n} }}"
      },
      "typeVersion": 1.4
    },
    {
      "id": "a4e446fe-2fb8-4806-8620-e06c4d639ec0",
      "name": "Manual Pending Approval Complete",
      "type": "n8n-nodes-base.noOp",
      "position": [
        2320,
        560
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "85103885-896d-48a7-9969-bf1f7859cd3e",
      "name": "Store Standard Triage Record in Airtable",
      "type": "n8n-nodes-base.airtable",
      "position": [
        1296,
        752
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Set Request Triage Config').first().json.airtable_base_id }}"
        },
        "table": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Set Request Triage Config').first().json.airtable_table_id }}"
        },
        "columns": {
          "value": {
            "Status": "In Triaging",
            "Due_Date": "={{ $json.due_date }}",
            "Objective": "={{ $json.objective }}",
            "Requester": "={{ $json.requester_name }}",
            "Created_At": "={{ $json.received_at }}",
            "Request_ID": "={{ $json.request_id }}",
            "Risk_Level": "={{ $json.triage_risk_level }}",
            "Next_Action": "Assign owner and confirm delivery handoff",
            "Deliverables": "={{ $json.deliverables }}",
            "Request_Type": "={{ $json.request_type }}",
            "Triage_Route": "={{ $json.triage_route }}",
            "Triage_Score": "={{ $json.triage_score }}",
            "Request_Title": "={{ $json.request_title }}",
            "Triage_Summary": "={{ $json.triage_summary }}",
            "Approval_Status": "Not Required",
            "Requester_Email": "={{ $json.requester_email }}",
            "Recommended_Owner": "={{ $json.recommended_owner_type || 'Delivery Lead' }}"
          },
          "schema": [],
          "mappingMode": "defineBelow",
          "matchingColumns": []
        },
        "options": {},
        "operation": "create"
      },
      "typeVersion": 2.1
    }
  ],
  "connections": {
    "Prepare Handoff": {
      "main": [
        [
          {
            "node": "Patch Airtable Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Brief Complete": {
      "main": [
        [
          {
            "node": "OpenAI Classify Request",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Prepare Clarification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Needs Approval": {
      "main": [
        [
          {
            "node": "Store Triage Record in Airtable",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Store Standard Triage Record in Airtable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Assign Triage Owner": {
      "main": [
        [
          {
            "node": "Prepare Handoff",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Approval Config": {
      "main": [
        [
          {
            "node": "Normalise Approval Decision",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Patch Airtable Status": {
      "main": [
        [
          {
            "node": "If Request Came From Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Clarification": {
      "main": [
        [
          {
            "node": "If Clarification Request Is Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compile Weekly Summary": {
      "main": [
        [
          {
            "node": "Email Weekly Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Classify Request": {
      "main": [
        [
          {
            "node": "Parse Classification Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Approval Update": {
      "main": [
        [
          {
            "node": "Patch Airtable Approval Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Brief Completeness": {
      "main": [
        [
          {
            "node": "If Brief Complete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Execution Trigger": {
      "main": [
        [
          {
            "node": "Set Example Request Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Example Request Data": {
      "main": [
        [
          {
            "node": "Set Request Triage Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Project Request": {
      "main": [
        [
          {
            "node": "Check Brief Completeness",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Request Triage Config": {
      "main": [
        [
          {
            "node": "Normalize Project Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Risk and Urgency": {
      "main": [
        [
          {
            "node": "If Needs Approval",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Open Triage Requests": {
      "main": [
        [
          {
            "node": "Compile Weekly Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalise Approval Decision": {
      "main": [
        [
          {
            "node": "Find Pending Airtable Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Classification Result": {
      "main": [
        [
          {
            "node": "Calculate Risk and Urgency",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Weekly Reporting Config": {
      "main": [
        [
          {
            "node": "Fetch Open Triage Requests",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Request Came From Webhook": {
      "main": [
        [
          {
            "node": "Send Triage Outcome",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Manual Triage Complete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Pending Airtable Request": {
      "main": [
        [
          {
            "node": "Prepare Approval Update",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Approval Request Data": {
      "main": [
        [
          {
            "node": "Post Approval to Slack",
            "type": "main",
            "index": 0
          },
          {
            "node": "If Pending Approval Request Is Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Weekly Triage Summary Trigger": {
      "main": [
        [
          {
            "node": "Set Weekly Reporting Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Project Request Received": {
      "main": [
        [
          {
            "node": "Set Request Triage Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Patch Airtable Approval Result": {
      "main": [
        [
          {
            "node": "Send Approval Outcome",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Triage Record in Airtable": {
      "main": [
        [
          {
            "node": "Prepare Approval Request Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Approval Decision Received": {
      "main": [
        [
          {
            "node": "Set Approval Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Clarification Request Is Webhook": {
      "main": [
        [
          {
            "node": "Send Clarification Needed Response",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Manual Clarification Complete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Pending Approval Request Is Webhook": {
      "main": [
        [
          {
            "node": "Send Pending Approval Response",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Manual Pending Approval Complete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Standard Triage Record in Airtable": {
      "main": [
        [
          {
            "node": "Assign Triage Owner",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "active": false
}
