{
  "name": "REVOPS-L1-001 | 01 | Inbound intake",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "revops-l1-001-inbound",
        "authentication": "headerAuth",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "f155755c-ce24-4ec8-b6df-4bf4b5b706cc",
      "name": "Inbound Form",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "webhookId": "0f96f51d-bfe0-480f-b348-aaedc8ff996b",
      "credentials": {
        "httpHeaderAuth": {
          "id": "REPLACE_WEBHOOK_CREDENTIAL",
          "name": "REVOPS Form Secret"
        }
      }
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const raw = $input.first().json.body;\nconst errors = [];\nif (!raw || typeof raw !== 'object' || Array.isArray(raw)) return [{json:{ok:false,errors:['JSON object required']}}];\nfunction text(key, max, multiline = false) {\n  const v = raw[key];\n  if (v == null) return '';\n  if (typeof v !== 'string') { errors.push(key + ' must be a string'); return ''; }\n  let s = v.normalize('NFKC').replace(/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f]/g, '').trim();\n  s = multiline ? s.replace(/\\r\\n?/g, '\\n') : s.replace(/\\s+/g, ' ');\n  if (s.length > max) errors.push(key + ' exceeds ' + max + ' characters');\n  return s;\n}\nconst submission_id = text('submission_id', 36).toLowerCase();\nif (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(submission_id)) errors.push('submission_id must be a stable UUID generated by the form backend');\nconst email = text('email',254).toLowerCase();\nif (!/^[^\\s<>@]+@[^\\s<>@]+\\.[^\\s<>@]+$/.test(email)) errors.push('A syntactically valid email is required');\nconst payload = {submission_id,email,first_name:text('first_name',100),last_name:text('last_name',100),company:text('company',200),phone:text('phone',80),job_title:text('job_title',200),message:text('message',5000,true),service_interest:text('service_interest',200),budget:text('budget',200),timeline:text('timeline',200),form_id:text('form_id',100),page_url:text('page_url',1000),response_requested:raw.response_requested === true};\nreturn [{json:{ok:errors.length === 0,errors,tenant:'REVOPS-L1-001',payload}}];"
      },
      "id": "351ea610-5c62-4cce-8b0c-342f11639352",
      "name": "Normalize Form",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        260,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "2002464e-2c80-42ff-a29b-a2b74ffddb25",
              "leftValue": "={{ $json.ok === true }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "6e56374d-269a-4397-bdec-a9db72151c9d",
      "name": "Input Valid?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        520,
        0
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT * FROM revops.enqueue($1::text,$2::uuid,$3::text,$4::jsonb);",
        "options": {
          "queryReplacement": "={{ [$json.tenant,$json.payload.submission_id,$json.payload.email,JSON.stringify($json.payload)] }}"
        }
      },
      "id": "f560a6ea-007b-43b4-8b53-0f8543b645a1",
      "name": "Enqueue Once",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        780,
        0
      ],
      "credentials": {
        "postgres": {
          "id": "REPLACE_POSTGRES_CREDENTIAL",
          "name": "REVOPS Postgres"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 2000
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ {accepted:$json.accepted,duplicate:!$json.inserted,submission_id:$json.job_id} }}",
        "options": {
          "responseCode": "={{ $json.accepted ? 202 : 409 }}"
        }
      },
      "id": "8cb4a82b-b278-451d-9131-d2eb1d671e61",
      "name": "Acknowledge Queue",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        1040,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "22cdcbde-d0d7-42cd-b705-26da9d3ba954",
              "leftValue": "={{ $('Enqueue Once').first().json.inserted === true }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "0474f2ab-bd02-4279-a190-791712c96853",
      "name": "New Queue Entry?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1300,
        0
      ]
    },
    {
      "parameters": {
        "source": "database",
        "workflowId": {
          "__rl": true,
          "value": "REPLACE_WORKER_WORKFLOW_ID",
          "mode": "id"
        },
        "mode": "once",
        "options": {
          "waitForSubWorkflow": false
        }
      },
      "id": "8d91e64b-c794-435a-8ba8-7696860acf48",
      "name": "Start Lead Worker",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1.3,
      "position": [
        1560,
        0
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ {accepted:false,errors:$json.errors} }}",
        "options": {
          "responseCode": 400
        }
      },
      "id": "e87e1862-51f9-4d09-ae05-c795e3ccecab",
      "name": "Reject Input",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        1820,
        0
      ]
    },
    {
      "parameters": {
        "content": "## 01 — Durable intake\nAuthenticate from your form backend. Never embed the secret in browser code.\nStable submission_id UUID = webhook replay protection.\n202 means safely queued, not emailed. 409 means the UUID was reused with different data.\nRun database-setup.sql before testing.",
        "width": 650,
        "height": 220
      },
      "id": "9583e24e-07cf-4e1b-a4af-87109c8c81a7",
      "name": "Guide 8",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -260
      ]
    }
  ],
  "connections": {
    "Inbound Form": {
      "main": [
        [
          {
            "node": "Normalize Form",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Form": {
      "main": [
        [
          {
            "node": "Input Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Enqueue Once": {
      "main": [
        [
          {
            "node": "Acknowledge Queue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Input Valid?": {
      "main": [
        [
          {
            "node": "Enqueue Once",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Reject Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Acknowledge Queue": {
      "main": [
        [
          {
            "node": "New Queue Entry?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "New Queue Entry?": {
      "main": [
        [
          {
            "node": "Start Lead Worker",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "timezone": "UTC",
    "saveManualExecutions": true,
    "saveExecutionProgress": true
  },
  "active": false,
  "pinData": {},
  "tags": []
}

