{
  "name": "REVOPS-LI-005 | 01 | Duplicate scan intake",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "revops-li-005-scan",
        "authentication": "headerAuth",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "ba99b7a0-77b6-4443-8bcf-a73e2974f741",
      "name": "Duplicate Scan Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "webhookId": "b1064782-ab2f-4e42-b7ef-80c3ed8f228b",
      "credentials": {
        "httpHeaderAuth": {
          "id": "REPLACE_SCAN_SECRET_CREDENTIAL",
          "name": "Duplicate Scan Secret"
        }
      }
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const raw=$input.first().json.body;const errors=[];if(!raw||typeof raw!=='object'||Array.isArray(raw))return [{json:{valid:false,errors:['JSON object required']}}];if(!/^[0-9a-f-]{36}$/i.test(String(raw.event_id||'')))errors.push('event_id UUID required');if(!['contacts','companies'].includes(raw.object_type))errors.push('object_type must be contacts or companies');if(!/^\\d+$/.test(String(raw.record_id||'')))errors.push('numeric HubSpot record_id required');if(JSON.stringify(raw).length>8192)errors.push('request exceeds 8 KiB');return [{json:{valid:!errors.length,errors,raw}}];"
      },
      "id": "97c75d0a-195a-463a-8f00-fb8a486a6c08",
      "name": "Validate Scan Request",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        270,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "3e09a60b-8f3d-456d-a64b-9b9313239de9",
              "leftValue": "={{ $json.valid === true }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "daa37ac8-8491-4447-9f87-75580e96ebbc",
      "name": "Request Valid?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        540,
        0
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT * FROM duplicate_cleanup.enqueue($1::uuid,$2::text,$3::text,$4::jsonb);",
        "options": {
          "queryReplacement": "={{ [$json.raw.event_id,$json.raw.object_type,$json.raw.record_id,JSON.stringify($json.raw)] }}"
        }
      },
      "id": "f7257cd4-6879-429e-9617-9638edcbbba7",
      "name": "Capture Before Processing",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        810,
        0
      ],
      "credentials": {
        "postgres": {
          "id": "REPLACE_POSTGRES_CREDENTIAL",
          "name": "ATTRIBUTION Postgres"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ {accepted:$json.accepted,inserted:$json.inserted,event_id:$json.event_id} }}",
        "options": {
          "responseCode": "={{ $json.accepted ? 202 : 409 }}"
        }
      },
      "id": "30aa2c2c-f788-410a-8bd9-65d2576bbbaa",
      "name": "Accepted",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        1080,
        0
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ {accepted:false,errors:$json.errors} }}",
        "options": {
          "responseCode": 400
        }
      },
      "id": "400b3032-7263-42e1-b1bd-1d360e77d94f",
      "name": "Rejected",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        1350,
        0
      ]
    },
    {
      "parameters": {
        "source": "database",
        "workflowId": {
          "__rl": true,
          "value": "REPLACE_DUPLICATE_DETECTOR_WORKFLOW_ID",
          "mode": "id"
        },
        "mode": "once",
        "options": {
          "waitForSubWorkflow": false
        }
      },
      "id": "6b5597d2-d95a-403a-b5c4-0592849feb5c",
      "name": "Start Duplicate Detector",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1.3,
      "position": [
        1620,
        0
      ]
    },
    {
      "parameters": {
        "content": "## Authenticated, idempotent intake\nSend only a HubSpot object type, record ID and stable event UUID.\nThe workflow stores the raw request before reading HubSpot. A reused UUID with a different payload is rejected.\nBind Start Duplicate Detector to workflow 02 after import.",
        "width": 700,
        "height": 220
      },
      "id": "eb9ba5de-fb4b-4a19-b831-bf8f0905540f",
      "name": "Workflow Guide",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -270
      ]
    }
  ],
  "connections": {
    "Duplicate Scan Webhook": {
      "main": [
        [
          {
            "node": "Validate Scan Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Scan Request": {
      "main": [
        [
          {
            "node": "Request Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Request Valid?": {
      "main": [
        [
          {
            "node": "Capture Before Processing",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Rejected",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Capture Before Processing": {
      "main": [
        [
          {
            "node": "Accepted",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Accepted": {
      "main": [
        [
          {
            "node": "Start Duplicate Detector",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "timezone": "Asia/Tbilisi",
    "saveExecutionProgress": true
  },
  "active": false,
  "pinData": {},
  "tags": []
}
