{
  "name": "REVOPS-LI-007 | 03 | High-value follow-up approvals",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "GET",
        "path": "revops-li-007-pending",
        "authentication": "headerAuth",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "f7912208-e6b4-4ff6-8b02-2dd8b6e29005",
      "name": "List Pending Follow-Ups",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "webhookId": "c4b922a8-d935-491f-80b7-6f5309acf5e1",
      "credentials": {
        "httpHeaderAuth": {
          "id": "REPLACE_APPROVAL_SECRET_CREDENTIAL",
          "name": "Proposal Approval Secret"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT ghosted_proposal.pending_approvals() AS cases;",
        "options": {
          "queryReplacement": "={{ [] }}"
        }
      },
      "id": "a53a641d-78f1-4993-ab40-c98ce3c6f011",
      "name": "Read Pending Follow-Ups",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        270,
        0
      ],
      "credentials": {
        "postgres": {
          "id": "REPLACE_POSTGRES_CREDENTIAL",
          "name": "ATTRIBUTION Postgres"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ {cases:$json.cases} }}",
        "options": {
          "responseCode": 200
        }
      },
      "id": "93feae07-c48c-4c1f-a0dc-4fc674cfad1e",
      "name": "Return Pending Follow-Ups",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        540,
        0
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "revops-li-007-approval",
        "authentication": "headerAuth",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "ec10efb8-52cb-43dd-bdd9-7cd00a0e0ed0",
      "name": "Approval Decision",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        810,
        0
      ],
      "webhookId": "e22bd028-8eec-4811-8922-84cc86c1f9ce",
      "credentials": {
        "httpHeaderAuth": {
          "id": "REPLACE_APPROVAL_SECRET_CREDENTIAL",
          "name": "Proposal Approval Secret"
        }
      }
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const b=$input.first().json.body||{},errors=[];if(!/^[0-9a-f-]{36}$/i.test(String(b.case_id||'')))errors.push('case_id UUID required');if(!/^[0-9a-f-]{36}$/i.test(String(b.approval_token||'')))errors.push('approval_token UUID required');if(!['APPROVE','REJECT'].includes(b.decision))errors.push('decision must be APPROVE or REJECT');if(!/^\\S+@\\S+\\.\\S+$/.test(String(b.approver_email||'')))errors.push('approver_email required');return [{json:{valid:!errors.length,errors,b}}];"
      },
      "id": "a0a50c53-188d-4890-b31c-8ba51e17a589",
      "name": "Validate Approval Decision",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1080,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "9245a3fb-f3d6-4bf4-9166-ac85cd4187ef",
              "leftValue": "={{ $json.valid === true }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "f99d3c5e-1653-45be-9720-69872dc99a75",
      "name": "Approval Valid?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1350,
        0
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT ghosted_proposal.decide($1::uuid,$2::uuid,$3::text,$4::text,$5::text) AS result;",
        "options": {
          "queryReplacement": "={{ [$json.b.case_id,$json.b.approval_token,$json.b.decision,$json.b.approver_email,String($json.b.note||'')] }}"
        }
      },
      "id": "05602dc8-a6c9-4498-9110-361123c1a613",
      "name": "Apply Approval Decision",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        1620,
        0
      ],
      "credentials": {
        "postgres": {
          "id": "REPLACE_POSTGRES_CREDENTIAL",
          "name": "ATTRIBUTION Postgres"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ $json.result }}",
        "options": {
          "responseCode": 200
        }
      },
      "id": "1a973bb4-01d9-4c01-b8f9-58e0701bd884",
      "name": "Return Approval Result",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        1890,
        0
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ {accepted:false,errors:$json.errors} }}",
        "options": {
          "responseCode": 400
        }
      },
      "id": "c3314598-9ec8-4b6c-88b6-b04cb5f44be5",
      "name": "Reject Invalid Decision",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        0,
        300
      ]
    },
    {
      "parameters": {
        "content": "## Protected approval API\nGET lists high-value pending cases and one-time tokens. POST approves or rejects.\nTokens expire after seven days and are rotated after use. Approval makes a draft eligible for the sender; the sender still revalidates HubSpot immediately before delivery.",
        "width": 700,
        "height": 220
      },
      "id": "a2bf6204-9e48-4a24-b699-2eaa6561d09c",
      "name": "Workflow Guide",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -270
      ]
    }
  ],
  "connections": {
    "List Pending Follow-Ups": {
      "main": [
        [
          {
            "node": "Read Pending Follow-Ups",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Pending Follow-Ups": {
      "main": [
        [
          {
            "node": "Return Pending Follow-Ups",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Approval Decision": {
      "main": [
        [
          {
            "node": "Validate Approval Decision",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Approval Decision": {
      "main": [
        [
          {
            "node": "Approval Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Approval Valid?": {
      "main": [
        [
          {
            "node": "Apply Approval Decision",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Reject Invalid Decision",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Apply Approval Decision": {
      "main": [
        [
          {
            "node": "Return Approval Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "timezone": "Asia/Tbilisi",
    "saveExecutionProgress": true
  },
  "active": false,
  "pinData": {},
  "tags": []
}
