{
  "name": "Screen CVs with OpenAI and PostgreSQL using chained prompts",
  "nodes": [
    {
      "id": "0cb0572a-b6e0-49e4-a9f2-c7249f9df88a",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -272,
        64
      ],
      "parameters": {
        "width": 500,
        "height": 832,
        "content": "## 🤖 AI CV Screening with Chained Prompts\n\nAutomatically screen resumes using 4 sequential AI prompts, each building on the previous one's output. Results are saved directly to PostgreSQL — no externa"
      }
    },
    {
      "id": "5ef87983-e5bb-49f1-9ea7-7842acb476d2",
      "name": "Database Schema",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        800,
        672
      ],
      "parameters": {
        "width": 708,
        "height": 880,
        "content": "## 🗄️ Required Database Schema\n\n```sql\nCREATE TABLE jobs (\n  id SERIAL PRIMARY KEY,\n  title VARCHAR(255) NOT NULL,\n  description TEXT NOT NULL,\n  gabarito JSONB,\n  status VARCHAR(20) DEFAULT 'draft',\n"
      }
    },
    {
      "id": "e04df0d8-215c-4c7c-9c76-e49cd768cbdf",
      "name": "Input Format",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        304,
        64
      ],
      "parameters": {
        "width": 340,
        "height": 232,
        "content": "## 📥 Webhook Input\nSend a POST request:\n```json\n{\n  \"job_id\": 1,\n  \"candidate_ids\": [1, 2, 3]\n}\n```\nCandidates and job must already exist in the database with `cv_text` populated."
      }
    },
    {
      "id": "e3297343-eac2-4658-a1a7-19d66f2df74f",
      "name": "Prompt 0 Section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        832,
        176
      ],
      "parameters": {
        "width": 700,
        "height": 140,
        "content": "## 🧠 Prompt 0 — Job Template Extraction\nRuns only when `gabarito` is null in the jobs table.\nExtracts structured requirements and sets weights automatically."
      }
    },
    {
      "id": "68d2ff58-4421-422b-8a6b-008b40527bde",
      "name": "Candidate Loop Section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2112,
        64
      ],
      "parameters": {
        "width": 1700,
        "height": 140,
        "content": "## 🔄 Candidate Loop\nProcesses one candidate at a time.\nPrompts 1 → 2 → 3 run sequentially,\neach using the previous output as context.\nResults saved to Postgres after each candidate."
      }
    },
    {
      "id": "512b3240-cb2c-4d8a-9ad7-be87df671b62",
      "name": "Summary Section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4432,
        64
      ],
      "parameters": {
        "width": 1100,
        "height": 140,
        "content": "## 📊 Executive Summary — Prompt 4\nRuns once when all candidates are processed (pending = 0).\nSaves pool-level recommendation to job_summaries table."
      }
    },
    {
      "id": "7b48fa44-ea08-4c46-b89e-388d3ae9ee0e",
      "name": "Receive CVs",
      "type": "n8n-nodes-base.webhook",
      "position": [
        304,
        360
      ]
    },
    {
      "id": "6dbd0cb2-dba2-46dd-a355-eb8bb474e68b",
      "name": "Fetch Job and Candidates",
      "type": "n8n-nodes-base.postgres",
      "position": [
        528,
        360
      ]
    },
    {
      "id": "4cfed364-6856-4b72-a781-dbcc5e784879",
      "name": "Job Template exists?",
      "type": "n8n-nodes-base.if",
      "position": [
        752,
        360
      ]
    },
    {
      "id": "e0574622-0404-4739-8f86-64e9f2061a5a",
      "name": "Prompt 0 — Extract Job Template",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        976,
        432
      ]
    },
    {
      "id": "1f095e0e-bb1e-4eb7-b2bf-593e775928bb",
      "name": "Save Job Template",
      "type": "n8n-nodes-base.postgres",
      "position": [
        1328,
        432
      ]
    },
    {
      "id": "aed6cd32-e56a-482a-85ec-3a3ccde8e35f",
      "name": "Prepare Candidates",
      "type": "n8n-nodes-base.code",
      "position": [
        1552,
        360
      ]
    },
    {
      "id": "d974a929-57f3-49de-821b-77a41d46724e",
      "name": "Loop Candidates",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1776,
        360
      ]
    },
    {
      "id": "414e3332-8857-4b7a-8f19-4083e60516a1",
      "name": "Prompt 1 — Score",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        2000,
        288
      ]
    },
    {
      "id": "ab0906db-3de1-4f4e-b749-a680b16d8cd9",
      "name": "Prompt 2 — Gaps",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        2352,
        288
      ]
    },
    {
      "id": "bccbda40-5c02-490b-b2bd-ee1a98ad1e9e",
      "name": "Prompt 3 — Interview Questions",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        2704,
        288
      ]
    },
    {
      "id": "01fcae4e-6bcb-48a8-bbd0-8e137e9ef190",
      "name": "Build Analysis Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        3056,
        288
      ]
    },
    {
      "id": "d9ba1c99-fb5d-46f4-8af1-62a9189b4c66",
      "name": "Save Analysis",
      "type": "n8n-nodes-base.postgres",
      "position": [
        3280,
        288
      ]
    },
    {
      "id": "a74272c7-8c2d-4b3b-a6ac-baa509eb8b12",
      "name": "Update Candidate Status",
      "type": "n8n-nodes-base.postgres",
      "position": [
        3504,
        288
      ]
    },
    {
      "id": "25b0eaf5-2598-48d0-8be9-fb569d37ea03",
      "name": "Check Pending Candidates",
      "type": "n8n-nodes-base.postgres",
      "position": [
        3728,
        288
      ]
    },
    {
      "id": "3398f657-1294-4e5f-a006-6f8ed5c2e9bc",
      "name": "All Candidates Processed?",
      "type": "n8n-nodes-base.if",
      "position": [
        3952,
        360
      ]
    },
    {
      "id": "397c9184-d234-47d8-bc3a-49087c7874e1",
      "name": "Update Job Status",
      "type": "n8n-nodes-base.postgres",
      "position": [
        4176,
        360
      ]
    },
    {
      "id": "bb43a8a2-8431-47dd-b436-2f71c96430a6",
      "name": "Fetch Full Pool",
      "type": "n8n-nodes-base.postgres",
      "position": [
        4400,
        360
      ]
    },
    {
      "id": "e4934c9f-1b20-4bbd-b7d8-2512c665f51a",
      "name": "Fetch Job for Summary",
      "type": "n8n-nodes-base.postgres",
      "position": [
        4624,
        360
      ]
    },
    {
      "id": "f8529e9e-1443-4b42-a6c7-c59af19f51c6",
      "name": "Prompt 4 — Executive Summary",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        4848,
        360
      ]
    },
    {
      "id": "44192b37-dd99-4a6a-a23b-8138b1b33d28",
      "name": "Build Summary Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        5200,
        360
      ]
    },
    {
      "id": "221e40ba-5662-45ac-9200-787a460e9c8b",
      "name": "Save Summary",
      "type": "n8n-nodes-base.postgres",
      "position": [
        5424,
        360
      ]
    }
  ],
  "connections": {
    "Receive CVs": {
      "main": [
        [
          {
            "node": "Fetch Job and Candidates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Analysis": {
      "main": [
        [
          {
            "node": "Update Candidate Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Full Pool": {
      "main": [
        [
          {
            "node": "Fetch Job for Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Candidates": {
      "main": [
        [],
        [
          {
            "node": "Prompt 1 — Score",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prompt 2 — Gaps": {
      "main": [
        [
          {
            "node": "Prompt 3 — Interview Questions",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Job Template": {
      "main": [
        [
          {
            "node": "Prepare Candidates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Job Status": {
      "main": [
        [
          {
            "node": "Fetch Full Pool",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Candidates": {
      "main": [
        [
          {
            "node": "Loop Candidates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prompt 1 — Score": {
      "main": [
        [
          {
            "node": "Prompt 2 — Gaps",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Job Template exists?": {
      "main": [
        [
          {
            "node": "Prepare Candidates",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Prompt 0 — Extract Job Template",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Summary Payload": {
      "main": [
        [
          {
            "node": "Save Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Job for Summary": {
      "main": [
        [
          {
            "node": "Prompt 4 — Executive Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Analysis Payload": {
      "main": [
        [
          {
            "node": "Save Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Candidate Status": {
      "main": [
        [
          {
            "node": "Check Pending Candidates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Pending Candidates": {
      "main": [
        [
          {
            "node": "All Candidates Processed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Job and Candidates": {
      "main": [
        [
          {
            "node": "Job Template exists?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "All Candidates Processed?": {
      "main": [
        [
          {
            "node": "Update Job Status",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Loop Candidates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prompt 4 — Executive Summary": {
      "main": [
        [
          {
            "node": "Build Summary Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prompt 3 — Interview Questions": {
      "main": [
        [
          {
            "node": "Build Analysis Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prompt 0 — Extract Job Template": {
      "main": [
        [
          {
            "node": "Save Job Template",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}