{
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "name": "Find and enrich people records from form submissions into Notion",
  "nodes": [
    {
      "id": "6828091e-ea35-41ce-b08f-87ddc90a367d",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2608,
        224
      ],
      "parameters": {
        "width": 480,
        "height": 896,
        "content": "## Find and enrich people records from form submissions into Notion\n\n### How it works\n\n1. A form submission triggers the workflow, capturing name, email, and phone details to search for.\n2. Default search parameters are configured and a people-finder scrape job is submitted to the ScraperCity API.\n3. The workflow stores the returned run ID and enters a polling loop, waiting 60 seconds between each status check.\n4. Once the scrape job is confirmed complete, enriched results are downloaded from the API.\n5. Results are parsed, formatted, and deduplicated to produce clean person records.\n6. Each unique person record is saved as a new entry in a Notion database.\n\n### Setup steps\n\n- [ ] Create a ScraperCity account and obtain an API key for the people-finder endpoint\n- [ ] Configure the ScraperCity API credentials in n8n (used by Submit People Finder Job, Check Scrape Job Status, and Download Enriched Results nodes)\n- [ ] Create a Notion integration and share the target database with it, then add Notion credentials in n8n\n- [ ] Update the Notion node to point to your specific database and map the desired fields (name, email, phone, etc.)\n- [ ] Adjust the 'Configure Search Defaults' node to set appropriate max_results and any other default values for your use case\n- [ ] Test the form submission end-to-end with a known person to verify records are created correctly in Notion\n\n### Customization\n\nIncrease or decrease the 60-second wait interval in 'Wait 60 Seconds Before Poll' based on typical job completion times. Modify the 'Parse and Format Records' code node to extract additional fields returned by the API. Add a filter node after deduplication to only save records meeting a minimum data-quality threshold."
      },
      "typeVersion": 1
    },
    {
      "id": "919f26ae-0362-49b1-a517-7307c6d4402f",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2048,
        240
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 320,
        "content": "## Form input and setup\n\nAccepts a form submission from a user and sets default search parameters (name, email, phone, max results) before the job is dispatched."
      },
      "typeVersion": 1
    },
    {
      "id": "ec9d722b-468d-4681-9a8e-519e663c825d",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1552,
        240
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 320,
        "content": "## Job submission and ID storage\n\nPOSTs the search request to the ScraperCity people-finder API and stores the returned run ID for use in the polling loop."
      },
      "typeVersion": 1
    },
    {
      "id": "75c47616-a416-49b2-b60b-d23728e57b1f",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1040,
        272
      ],
      "parameters": {
        "color": 7,
        "width": 944,
        "height": 304,
        "content": "## Polling loop and status check\n\nRepeatedly waits 60 seconds then polls the ScraperCity API for job status, looping back until the scrape is confirmed complete."
      },
      "typeVersion": 1
    },
    {
      "id": "092afe92-f4bc-411a-8f83-7ab8f94f3a46",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -48,
        272
      ],
      "parameters": {
        "color": 7,
        "width": 688,
        "height": 304,
        "content": "## Results download and processing\n\nDownloads the enriched results file, parses and formats each person record with custom code, then removes any duplicates."
      },
      "typeVersion": 1
    },
    {
      "id": "38e08297-9ebd-4b75-9664-5c98559fe58c",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        704,
        224
      ],
      "parameters": {
        "color": 7,
        "width": 240,
        "height": 336,
        "content": "## Notion record creation\n\nSaves each unique, enriched person record as a new page in the configured Notion database."
      },
      "typeVersion": 1
    },
    {
      "id": "a1000000-0000-0000-0000-000000000010",
      "name": "People Finder Form",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -2000,
        400
      ],
      "parameters": {
        "options": {},
        "formTitle": "People Finder -- Skip Trace Search",
        "formFields": {
          "values": [
            {
              "fieldType": "text",
              "fieldLabel": "Full Name",
              "placeholder": "e.g. Jane Smith",
              "requiredField": false
            },
            {
              "fieldType": "email",
              "fieldLabel": "Email Address",
              "placeholder": "e.g. user@example.com",
              "requiredField": false
            },
            {
              "fieldType": "text",
              "fieldLabel": "Phone Number",
              "placeholder": "e.g. 5551234567",
              "requiredField": false
            }
          ]
        },
        "formDescription": "Enter at least one field to begin a people search. Results will be saved to Notion automatically."
      },
      "typeVersion": 2.2
    },
    {
      "id": "a1000000-0000-0000-0000-000000000011",
      "name": "Configure Search Defaults",
      "type": "n8n-nodes-base.set",
      "position": [
        -1750,
        400
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cfg-001",
              "name": "search_name",
              "type": "string",
              "value": "={{ $json['Full Name'] ?? '' }}"
            },
            {
              "id": "cfg-002",
              "name": "search_email",
              "type": "string",
              "value": "={{ $json['Email Address'] ?? '' }}"
            },
            {
              "id": "cfg-003",
              "name": "search_phone",
              "type": "string",
              "value": "={{ $json['Phone Number'] ?? '' }}"
            },
            {
              "id": "cfg-004",
              "name": "max_results",
              "type": "number",
              "value": 5
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "a1000000-0000-0000-0000-000000000020",
      "name": "Submit People Finder Job",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1500,
        400
      ],
      "parameters": {
        "url": "https://app.scrapercity.com/api/v1/scrape/people-finder",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"name\": {{ $json.search_name ? '[\"' + $json.search_name + '\"]' : '[]' }},\n  \"email\": {{ $json.search_email ? '[\"' + $json.search_email + '\"]' : '[]' }},\n  \"phone_number\": {{ $json.search_phone ? '[\"' + $json.search_phone + '\"]' : '[]' }},\n  \"street_citystatezip\": [],\n  \"max_results\": {{ $json.max_results }}\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "a1000000-0000-0000-0000-000000000021",
      "name": "Store Run ID",
      "type": "n8n-nodes-base.set",
      "position": [
        -1250,
        400
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "rid-001",
              "name": "runId",
              "type": "string",
              "value": "={{ $json.runId }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "a1000000-0000-0000-0000-000000000030",
      "name": "Loop Controller",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -1000,
        400
      ],
      "parameters": {
        "options": {
          "reset": false
        },
        "batchSize": 1
      },
      "typeVersion": 3,
      "alwaysOutputData": false
    },
    {
      "id": "a1000000-0000-0000-0000-000000000031",
      "name": "Wait 60 Seconds Before Poll",
      "type": "n8n-nodes-base.wait",
      "position": [
        -750,
        400
      ],
      "parameters": {
        "unit": "seconds",
        "amount": 60
      },
      "typeVersion": 1.1
    },
    {
      "id": "a1000000-0000-0000-0000-000000000032",
      "name": "Check Scrape Job Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -500,
        400
      ],
      "parameters": {
        "url": "=https://app.scrapercity.com/api/v1/scrape/status/{{ $('Store Run ID').item.json.runId }}",
        "method": "GET",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "a1000000-0000-0000-0000-000000000033",
      "name": "Is Scrape Complete?",
      "type": "n8n-nodes-base.if",
      "position": [
        -250,
        400
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-001",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "SUCCEEDED"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a1000000-0000-0000-0000-000000000040",
      "name": "Download Enriched Results",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        0,
        400
      ],
      "parameters": {
        "url": "=https://app.scrapercity.com/api/downloads/{{ $('Store Run ID').item.json.runId }}",
        "method": "GET",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "a1000000-0000-0000-0000-000000000041",
      "name": "Parse and Format Records",
      "type": "n8n-nodes-base.code",
      "position": [
        250,
        400
      ],
      "parameters": {
        "jsCode": "/**\n * Parse ScraperCity People Finder download response.\n * The API returns either a JSON array or an object with a 'data' or 'results' key.\n * Output: one item per person record with normalised fields.\n */\nconst raw = $input.first().json;\n\nlet records = [];\nif (Array.isArray(raw)) {\n  records = raw;\n} else if (Array.isArray(raw.data)) {\n  records = raw.data;\n} else if (Array.isArray(raw.results)) {\n  records = raw.results;\n} else {\n  // Wrap single object\n  records = [raw];\n}\n\nreturn records\n  .filter(r => r && (r.name || r.full_name || r.first_name))\n  .map(r => ({\n    json: {\n      full_name: r.full_name ?? [r.first_name, r.last_name].filter(Boolean).join(' ') ?? r.name ?? '',\n      email: r.email ?? r.emails?.[0] ?? '',\n      phone: r.phone ?? r.phone_number ?? r.phones?.[0] ?? '',\n      address: r.address ?? r.street_address ?? '',\n      city: r.city ?? '',\n      state: r.state ?? '',\n      zip: r.zip ?? r.postal_code ?? '',\n      age: r.age ?? '',\n      source_run_id: $('Store Run ID').item.json.runId\n    }\n  }));"
      },
      "typeVersion": 2
    },
    {
      "id": "a1000000-0000-0000-0000-000000000042",
      "name": "Remove Duplicate People",
      "type": "n8n-nodes-base.removeDuplicates",
      "position": [
        500,
        400
      ],
      "parameters": {
        "compare": "selectedFields",
        "options": {},
        "fieldsToCompare": {
          "fields": [
            {
              "fieldName": "full_name"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a1000000-0000-0000-0000-000000000043",
      "name": "Save Person Record to Notion",
      "type": "n8n-nodes-base.notion",
      "position": [
        750,
        400
      ],
      "parameters": {
        "title": "={{ $json.full_name }}",
        "options": {},
        "resource": "databasePage",
        "operation": "create",
        "databaseId": {
          "__rl": true,
          "mode": "id",
          "value": "="
        },
        "propertiesUi": {
          "propertyValues": [
            {
              "key": "Full Name|title",
              "textValue": "={{ $json.full_name }}"
            },
            {
              "key": "Email|rich_text",
              "textValue": "={{ $json.email }}"
            },
            {
              "key": "Phone|rich_text",
              "textValue": "={{ $json.phone }}"
            },
            {
              "key": "Address|rich_text",
              "textValue": "={{ [$json.address, $json.city, $json.state, $json.zip].filter(v => v).join(', ') }}"
            },
            {
              "key": "Age|rich_text",
              "textValue": "={{ $json.age }}"
            },
            {
              "key": "Run ID|rich_text",
              "textValue": "={{ $json.source_run_id }}"
            }
          ]
        }
      },
      "credentials": {
        "notionApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    }
  ],
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Store Run ID": {
      "main": [
        [
          {
            "node": "Loop Controller",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Controller": {
      "main": [
        [
          {
            "node": "Wait 60 Seconds Before Poll",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "People Finder Form": {
      "main": [
        [
          {
            "node": "Configure Search Defaults",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Scrape Complete?": {
      "main": [
        [
          {
            "node": "Download Enriched Results",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Loop Controller",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Scrape Job Status": {
      "main": [
        [
          {
            "node": "Is Scrape Complete?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Remove Duplicate People": {
      "main": [
        [
          {
            "node": "Save Person Record to Notion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse and Format Records": {
      "main": [
        [
          {
            "node": "Remove Duplicate People",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Submit People Finder Job": {
      "main": [
        [
          {
            "node": "Store Run ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Configure Search Defaults": {
      "main": [
        [
          {
            "node": "Submit People Finder Job",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Enriched Results": {
      "main": [
        [
          {
            "node": "Parse and Format Records",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 60 Seconds Before Poll": {
      "main": [
        [
          {
            "node": "Check Scrape Job Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}