AutomationFlowsAI & RAG › My Workflow Jgvj7vwvk7wvsgvp

My Workflow Jgvj7vwvk7wvsgvp

My-Workflow-Jgvj7Vwvk7Wvsgvp. Uses start, postgres, openAi, emailSend. Webhook trigger; 35 nodes.

Webhook trigger★★★★★ complexityAI-powered35 nodesStartPostgresOpenAIEmail SendGitHubSlack
AI & RAG Trigger: Webhook Nodes: 35 Complexity: ★★★★★ AI nodes: yes Added:

This workflow follows the Emailsend → OpenAI recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "updatedAt": "2025-09-23T08:27:24.000Z",
  "createdAt": "2025-08-11T18:02:34.196Z",
  "id": "JGVj7vwvk7WvsGvP",
  "name": "My workflow",
  "active": false,
  "isArchived": false,
  "nodes": [
    {
      "parameters": {},
      "name": "Start",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        -1600,
        304
      ],
      "id": "1611cbdf-5641-4f3e-9c0f-7cfabb7665b0"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "pyragogy/process",
        "options": {}
      },
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        -1408,
        304
      ],
      "id": "5dd5fcb3-5968-4669-b279-cb7db2d5d02b"
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT inet_server_addr(); Verifica connessione DB",
        "additionalFields": {}
      },
      "name": "Check DB Connection",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 1,
      "position": [
        -1200,
        304
      ],
      "id": "c2a4998b-776c-426f-b6f9-b0809e615849",
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "resource": "chat",
        "model": "gpt-4o",
        "options": {},
        "requestOptions": {}
      },
      "name": "Meta-Orchestrator",
      "type": "n8n-nodes-base.openAi",
      "typeVersion": 1,
      "position": [
        -1008,
        304
      ],
      "id": "731e5488-9ec8-42e5-8b47-f43fa8d2e39e",
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "functionCode": "// Analizza il piano di orchestrazione e imposta per il looping\nlet rawPlan = $json.choices[0].message.content;\nlet plan;\n\ntry {\n  plan = JSON.parse(rawPlan);\n} catch (e) {\n  // Se il parsing fallisce, assumi che sia una stringa di array grezza\n  plan = rawPlan;\n}\n\n// Estrai in modo sicuro la sequenza degli agenti:\n// Se 'plan' \u00e8 un oggetto e ha una chiave 'agents', usala.\n// Altrimenti, se 'plan' \u00e8 un array, usalo direttamente.\n// Altrimenti, predefinisci un array vuoto.\nconst agentSequence = Array.isArray(plan) ? plan : (plan && plan.agents && Array.isArray(plan.agents) ? plan.agents : []);\n\n// Memorizza la sequenza e l'indice corrente per il loop\n$workflow.agentSequence = agentSequence;\n$workflow.currentAgentIndex = 0;\n$workflow.redraftLoopCount = 0; // Inizializza il contatore dei cicli di rielaborazione\n\n// Passa i dati di input al primo agente, assicurandosi che $items[0].json.body esista\nconst initialInput = $items[0] && $items[0].json && $items[0].json.body ? $items[0].json.body : {};\n\nreturn [{ json: { ...initialInput, agentToRun: agentSequence[0] || null } }];"
      },
      "name": "Parse Orchestration Plan",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        -800,
        304
      ],
      "id": "c7559cb4-9ec4-41b1-8d9f-fe414c61e558"
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $workflow.currentAgentIndex < $workflow.agentSequence.length }}",
              "value2": true
            }
          ]
        }
      },
      "name": "More Agents to Run?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        -608,
        304
      ],
      "id": "aa433d5d-ef0c-42ae-b63f-b707fa594702"
    },
    {
      "parameters": {
        "functionCode": "// Ottieni il nome dell'agente corrente\nconst agentName = $workflow.agentSequence[$workflow.currentAgentIndex];\n\n// Prepara i dati per l'esecuzione dell'agente\n// Passa l'output del passo precedente (o l'input iniziale).\n// Se stiamo rielaborando, l'input dell'agente dovrebbe includere il feedback di rielaborazione.\nconst previousOutput = $json.output || $json.body.input; // Assumendo che l'output dell'agente sia memorizzato nella chiave 'output'\nconst agentInput = $json.redraftInput || previousOutput; // Usa redraftInput se presente, altrimenti previousOutput\n\nreturn [{ json: { ...$json, agentToRun: agentName, agentInput: agentInput } }];"
      },
      "name": "Prepare Agent Input",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        -400,
        208
      ],
      "id": "9e7b117a-9a2a-4f49-9e78-66e8bd145a1d"
    },
    {
      "parameters": {
        "mode": "json"
      },
      "name": "Route Agents with Switch",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 1,
      "position": [
        -208,
        208
      ],
      "id": "3181be02-2e59-483e-b5e5-9e7561d8e082"
    },
    {
      "parameters": {
        "resource": "chat",
        "model": "gpt-4o",
        "options": {},
        "requestOptions": {}
      },
      "name": "Summarizer Agent",
      "type": "n8n-nodes-base.openAi",
      "typeVersion": 1,
      "position": [
        0,
        -32
      ],
      "id": "a8e44d92-da29-4613-a1ed-7a6301c5ec1b",
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "resource": "chat",
        "model": "gpt-4o",
        "options": {},
        "requestOptions": {}
      },
      "name": "Synthesizer Agent",
      "type": "n8n-nodes-base.openAi",
      "typeVersion": 1,
      "position": [
        0,
        112
      ],
      "id": "467c5542-5f2c-4589-b707-fe455177d0e6",
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "resource": "chat",
        "model": "gpt-4o",
        "options": {},
        "requestOptions": {}
      },
      "name": "Peer Reviewer Agent",
      "type": "n8n-nodes-base.openAi",
      "typeVersion": 1,
      "position": [
        0,
        208
      ],
      "id": "a9b2d78d-1e6c-4256-ad52-98dd21cffd0b",
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "resource": "chat",
        "model": "gpt-4o",
        "options": {},
        "requestOptions": {}
      },
      "name": "Sensemaking Agent",
      "type": "n8n-nodes-base.openAi",
      "typeVersion": 1,
      "position": [
        0,
        304
      ],
      "id": "a9d0bc05-0b5b-403f-962c-29daa0618049",
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "resource": "chat",
        "model": "gpt-4o",
        "options": {},
        "requestOptions": {}
      },
      "name": "Prompt Engineer Agent",
      "type": "n8n-nodes-base.openAi",
      "typeVersion": 1,
      "position": [
        0,
        400
      ],
      "id": "1c32717e-1807-483d-9c15-3119149dae03",
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "resource": "chat",
        "model": "gpt-4o",
        "options": {},
        "requestOptions": {}
      },
      "name": "Onboarding/Explainer Agent",
      "type": "n8n-nodes-base.openAi",
      "typeVersion": 1,
      "position": [
        0,
        512
      ],
      "id": "2550f8d1-24e3-4384-9cd1-d09d82f9f230",
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "functionCode": "// Prepara i metadati per il contenuto dell'Handbook.\n// Assicurati che l'input originale contenga 'title' e 'tags' o imposta dei valori predefiniti.\nconst title = $json.body.title || 'Untitled Handbook Entry';\nconst tags = $json.body.tags || [];\nconst phase = $json.body.phase || 'draft'; // Fase iniziale, pu\u00f2 essere 'final' dopo l'approvazione\nconst rhythm = $json.body.rhythm || 'on-demand'; // Ritmo cognitivo\n\nreturn [{ json: { ...$json, handbookTitle: title, handbookTags: tags, handbookPhase: phase, handbookRhythm: rhythm } }];"
      },
      "name": "Add Handbook Metadata",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        0,
        608
      ],
      "id": "477a28c8-e9b2-4da2-ae40-8adf45785b97"
    },
    {
      "parameters": {
        "functionCode": "// Prepara il contenuto proposto dall'Archivista per la revisione umana, inclusa la formattazione YAML.\n// Assicurati che l'input dell'agente (il contenuto generato) sia disponibile.\nconst proposedContent = $json.agentInput;\nconst title = $json.handbookTitle;\nconst tags = $json.handbookTags;\nconst phase = $json.handbookPhase;\nconst rhythm = $json.handbookRhythm;\n\n// Costruisci il front-matter YAML\nconst yamlFrontMatter = `---\ntitle: \"${title.replace(/\"/g, '\\\"')}\"\ntags: [${tags.map(t => `\"${t.replace(/\"/g, '\\\"')}\"`).join(', ')}]\nphase: \"${phase}\"\nrhythm: \"${rhythm}\"\n---\n\n`;\n\nconst finalMarkdownContent = yamlFrontMatter + proposedContent;\n\nreturn [{ json: { ...$json, proposedContent: proposedContent, reviewTitle: title, reviewTags: tags, finalMarkdownContent: finalMarkdownContent } }];"
      },
      "name": "Generate Content for Review",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        208,
        608
      ],
      "id": "0b5c46ad-843d-4905-8d8e-e9dd762ebf8b"
    },
    {
      "parameters": {
        "functionCode": "// Genera un ID univoco per questa richiesta di revisione.\n// Questo ID verr\u00e0 usato per correlare la risposta del revisore con questa istanza del workflow.\nconst reviewId = crypto.randomUUID();\n\nreturn [{ json: { ...$json, reviewId: reviewId } }];"
      },
      "name": "Generate Review ID",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        400,
        608
      ],
      "id": "33c279cc-e085-402d-a951-aba94cbd9416"
    },
    {
      "parameters": {
        "fromEmail": "your-email@example.com",
        "toEmail": "human-reviewer@example.com",
        "subject": "Revisione Contenuto Pyragogy Handbook: {{ $json.reviewTitle }}",
        "text": "Ciao revisore,\n\n\u00c8 stato proposto un nuovo contenuto per l'Handbook:\n\n---\n{{ $json.proposedContent }}\n---\n\nTitolo: {{ $json.reviewTitle }}\nTags: {{ $json.reviewTags.join(', ') }}\n\nPer favore, clicca su uno dei seguenti link per approvare o rifiutare:\n\nApprova: your_n8n_url/webhook/pyragogy/review-feedback?reviewId={{ $json.reviewId }}&status=approved\nRifiuta: your_n8n_url/webhook/pyragogy/review-feedback?reviewId={{ $json.reviewId }}&status=rejected\n\nGrazie!",
        "html": "<h3>Revisione Contenuto Pyragogy Handbook: {{ $json.reviewTitle }}</h3>\n<p>Ciao revisore,</p>\n<p>\u00c8 stato proposto un nuovo contenuto per l'Handbook:</p>\n<hr>\n<pre>{{ $json.proposedContent }}</pre>\n<hr>\n<p><strong>Titolo:</strong> {{ $json.reviewTitle }}</p>\n<p><strong>Tags:</strong> {{ $json.reviewTags.join(', ') }}</p>\n<p>Per favore, clicca su uno dei seguenti link per approvare o rifiutare:</p>\n<p><a href=\"your_n8n_url/webhook/pyragogy/review-feedback?reviewId={{ $json.reviewId }}&status=approved\">Approva</a></p>\n<p><a href=\"your_n8n_url/webhook/pyragogy/review-feedback?reviewId={{ $json.reviewId }}&status=rejected\">Rifiuta</a></p>\n<p>Grazie!</p>",
        "options": {}
      },
      "name": "Send Review Request Email",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 1,
      "position": [
        608,
        608
      ],
      "id": "96fbe885-670b-49c4-b724-e44b42ab3451"
    },
    {
      "parameters": {},
      "name": "Wait for Human Approval",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1,
      "position": [
        800,
        608
      ],
      "id": "8c5b668e-e05e-41bd-bd46-fe4a2f7d74b8"
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.query.status === 'approved' }}",
              "value2": true
            }
          ]
        }
      },
      "name": "Human Decision Split",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        1008,
        608
      ],
      "id": "8195c68f-cb67-439f-8b7b-e883c39dcb3a"
    },
    {
      "parameters": {
        "table": "handbook_entries",
        "columns": "title, content, version, created_by, tags, phase, rhythm",
        "additionalFields": {}
      },
      "name": "Save to handbook_entries",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 1,
      "position": [
        1200,
        512
      ],
      "id": "94231ed6-1af0-4329-b584-879b82447f3e"
    },
    {
      "parameters": {
        "functionCode": "// Registra il contributo dell'agente dopo l'approvazione umana\nconst entryId = $json.id; // ID dall'inserimento in handbook_entries\nconst agentName = 'Archivist';\nconst contributionType = 'Archiving (Approved)';\nconst details = { input: $json.proposedContent, metadata: { title: $json.reviewTitle, version: $json.version, tags: $json.reviewTags, phase: $json.handbookPhase, rhythm: $json.handbookRhythm }, reviewStatus: 'approved' };\n\n$items[0].json.contribution = { entryId, agentName, contributionType, details };\nreturn $items;"
      },
      "name": "Prepare Approved Contribution Data",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        1408,
        512
      ],
      "id": "0e41cbca-d0ec-45fc-b6c9-5357057bca48"
    },
    {
      "parameters": {
        "table": "agent_contributions",
        "columns": "entry_id, agent_name, contribution_type, details",
        "additionalFields": {}
      },
      "name": "Save Agent Contribution (Approved)",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 1,
      "position": [
        1600,
        512
      ],
      "id": "0e7ff61f-e439-4225-9538-84be6a0e48fc"
    },
    {
      "parameters": {
        "functionCode": "// Genera il percorso del file GitHub con slug e timestamp per il versioning.\nconst chapterSlug = ($json.reviewTitle || 'untitled').replace(/[^a-zA-Z0-9]/g, '-').toLowerCase();\nconst timestamp = new Date().toISOString().replace(/[:.-]/g, ''); // Rimuove caratteri non validi per i nomi di file\nconst filePathWithVersion = `content/${chapterSlug}_v${timestamp}.md`;\n\nreturn [{ json: { ...$json, githubFilePath: filePathWithVersion } }];"
      },
      "name": "Generate GitHub File Path",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        1808,
        512
      ],
      "id": "eb6ae210-b74d-4c64-aec5-641955c6951a"
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $env.GITHUB_ACCESS_TOKEN }}",
              "value2": true
            }
          ]
        }
      },
      "name": "GitHub Enabled?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        2000,
        512
      ],
      "id": "37ef7cd3-eda2-49da-a533-5b4e28560c96"
    },
    {
      "parameters": {
        "resource": "file",
        "operation": "createUpdate",
        "owner": {
          "__rl": true,
          "value": "={{ $env.GITHUB_REPOSITORY_OWNER }}"
        },
        "repository": "={{ $env.GITHUB_REPOSITORY_NAME }}",
        "filePath": "={{ $json.githubFilePath }}"
      },
      "name": "Commit to GitHub (Approved)",
      "type": "n8n-nodes-base.github",
      "typeVersion": 1,
      "position": [
        2208,
        512
      ],
      "id": "a534edf4-f888-455c-979e-6f84f99fcd3d"
    },
    {
      "parameters": {
        "functionCode": "// Registra il rifiuto umano del contenuto\nconst agentName = 'Archivist';\nconst reviewId = $json.reviewId;\nconst reviewStatus = 'rejected';\nconst reviewComments = $json.query.comments || 'Nessun commento fornito.';\nconst proposedContent = $json.proposedContent;\nconst title = $json.reviewTitle;\n\nconsole.log(`Contenuto proposto dall'Archivista (ID: ${reviewId}, Titolo: ${title}) rifiutato dall'umano. Commenti: ${reviewComments}`);\n\n// Prepara l'output per indicare il rifiuto e consentire al flusso di continuare.\nreturn [{ json: { ...$json, reviewStatus: reviewStatus, reviewComments: reviewComments, output: { message: `Archivista: Contenuto rifiutato dall'umano.`, status: 'rejected', comments: reviewComments } } }];"
      },
      "name": "Log Human Rejection",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        1200,
        704
      ],
      "id": "c1130f1a-cdc6-42c6-999f-34f2ceeeeeee"
    },
    {
      "parameters": {
        "mode": "mergeByPropertyName"
      },
      "name": "Merge Archivist Paths",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 1,
      "position": [
        2400,
        608
      ],
      "id": "4ffbe6ee-b42b-41a0-858e-cfb0861ddd26"
    },
    {
      "parameters": {
        "functionCode": "// Valuta i flag 'major_issue' dagli agenti di revisione per determinare se \u00e8 necessaria una rielaborazione.\nlet majorIssueCount = 0;\nlet redraftFeedback = '';\n\n// Assumi che gli output degli agenti di revisione siano accessibili tramite $node\n// (Ad esempio, se Peer Reviewer -> Sensemaking -> Prompt Engineer sono sequenziali prima di questo nodo)\n\nif ($node[\"Peer Reviewer Agent\"] && $node[\"Peer Reviewer Agent\"].json && $node[\"Peer Reviewer Agent\"].json.choices && $node[\"Peer Reviewer Agent\"].json.choices[0] && $node[\"Peer Reviewer Agent\"].json.choices[0].message && $node[\"Peer Reviewer Agent\"].json.choices[0].message.content) {\n    const peerReviewOutput = JSON.parse($node[\"Peer Reviewer Agent\"].json.choices[0].message.content);\n    if (peerReviewOutput.major_issue) majorIssueCount++;\n    redraftFeedback += `Peer Reviewer: ${peerReviewOutput.suggestions || ''}\\n`;\n}\n\nif ($node[\"Sensemaking Agent\"] && $node[\"Sensemaking Agent\"].json && $node[\"Sensemaking Agent\"].json.choices && $node[\"Sensemaking Agent\"].json.choices[0] && $node[\"Sensemaking Agent\"].json.choices[0].message && $node[\"Sensemaking Agent\"].json.choices[0].message.content) {\n    const sensemakingOutput = JSON.parse($node[\"Sensemaking Agent\"].json.choices[0].message.content);\n    if (sensemakingOutput.major_issue) majorIssueCount++;\n    redraftFeedback += `Sensemaking Agent: ${sensemakingOutput.suggestions || ''}\\n`;\n}\n\nif ($node[\"Prompt Engineer Agent\"] && $node[\"Prompt Engineer Agent\"].json && $node[\"Prompt Engineer Agent\"].json.choices && $node[\"Prompt Engineer Agent\"].json.choices[0] && $node[\"Prompt Engineer Agent\"].json.choices[0].message && $node[\"Prompt Engineer Agent\"].json.choices[0].message.content) {\n    const promptEngineerOutput = JSON.parse($node[\"Prompt Engineer Agent\"].json.choices[0].message.content);\n    if (promptEngineerOutput.major_issue) majorIssueCount++;\n    redraftFeedback += `Prompt Engineer: ${promptEngineerOutput.suggestions || ''}\\n`;\n}\n\nconst redraftNeeded = majorIssueCount >= 2; // Voto a maggioranza\n\nreturn [{ json: { ...$json, redraftNeeded: redraftNeeded, redraftFeedback: redraftFeedback } }];"
      },
      "name": "Evaluate Board Consensus",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        208,
        304
      ],
      "id": "f72b6317-8321-4413-9a05-4369a968bc06"
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.redraftNeeded && $workflow.redraftLoopCount < 2 }}",
              "value2": true
            }
          ]
        }
      },
      "name": "Check Redraft Needed",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        400,
        304
      ],
      "id": "c050efd6-78b0-4bce-9289-2786011a4343"
    },
    {
      "parameters": {
        "functionCode": "// Gestisce la logica di rielaborazione: incrementa il contatore e reindirizza al Synthesizer.\n\n$workflow.redraftLoopCount += 1; // Incrementa il contatore del ciclo di rielaborazione\n\n// Trova l'indice del Synthesizer nella sequenza degli agenti\nconst synthesizerIndex = $workflow.agentSequence.indexOf(\"Synthesizer\");\n\n// Se il Synthesizer non \u00e8 il prossimo agente, imposta l'indice corrente per farlo ripartire dal Synthesizer.\n// Questo garantisce che il Synthesizer venga eseguito successivamente per la rielaborazione.\nif ($workflow.currentAgentIndex !== synthesizerIndex) {\n    $workflow.currentAgentIndex = synthesizerIndex;\n} else {\n    // Se siamo gi\u00e0 sul Synthesizer (es. dopo il primo passaggio del loop), assicurati che l'indice vada avanti normalmente nel prossimo ciclo.\n    // Questo \u00e8 un caso limite, di solito il Prepare Agent Input lo gestir\u00e0.\n}\n\n// Passa il feedback di rielaborazione come input per il Synthesizer.\n// Il nodo 'Prepare Agent Input' utilizzer\u00e0 questo campo per aggiornare 'agentInput'.\nreturn [{ json: { ...$json, redraftInput: $json.output + \"\\n\\nFeedback per la rielaborazione dal Peer Review Board:\\n\" + $json.redraftFeedback } }];"
      },
      "name": "Handle Redraft",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        608,
        208
      ],
      "id": "a47ac601-1a85-402b-8df2-8587a635d8e5"
    },
    {
      "parameters": {
        "functionCode": "// Ottieni il nome dell'agente appena eseguito\nconst agentName = $json.agentToRun;\nlet agentOutput = '';\n\n// Costruisci dinamicamente il nome del nodo per il recupero dell'output\nlet actualNodeName;\n\n// Gestione speciale per l'Archivista dopo la revisione umana\nif (agentName === 'Archivist') {\n    if ($json.query && $json.query.status === 'approved') {\n        agentOutput = { message: 'Contenuto archiviato con successo dopo l'approvazione umana.', entryId: $json.id || 'N/A', handbookMetadata: { title: $json.reviewTitle, tags: $json.reviewTags, phase: $json.handbookPhase, rhythm: $json.handbookRhythm } };\n    } else if ($json.query && $json.query.status === 'rejected') {\n        agentOutput = { message: 'Archiviazione rifiutata dall'umano.', reviewComments: $json.query.comments || 'Nessun commento' };\n    } else if ($json.reviewStatus === 'rejected') {\n        // Caso in cui la revisione umana \u00e8 stata rifiutata e si proviene dal ramo di rifiuto\n        agentOutput = { message: 'Archiviazione rifiutata dall'umano (tramite ramo di rifiuto).', reviewComments: $json.reviewComments || 'Nessun commento' };\n    } else {\n        agentOutput = { message: 'Processo Archivista: Attesa revisione umana o stato inatteso.', status: 'pending_review' };\n    }\n} else {\n    // Logica originale per gli altri agenti OpenAI\n    if (agentName === 'Onboarding/Explainer') {\n        actualNodeName = 'Onboarding/Explainer Agent';\n    } else {\n        actualNodeName = agentName + ' Agent';\n    }\n\n    // Tenta in modo sicuro di recuperare l'output dal nodo determinato dinamicamente\n    // Tenta anche di analizzare il JSON se l'output dell'agente \u00e8 un oggetto JSON (come per gli agenti di revisione)\n    let rawContent = '';\n    if ($node[actualNodeName] && $node[actualNodeName].json && $node[actualNodeName].json.choices && $node[actualNodeName].json.choices[0] && $node[actualNodeName].json.choices[0].message && $node[actualNodeName].json.choices[0].message.content) {\n        rawContent = $node[actualNodeName].json.choices[0].message.content;\n    } else {\n        console.warn(`Impossibile trovare l'output chat OpenAI standard per il nodo: ${actualNodeName}. Ritorno al JSON grezzo.`);\n        agentOutput = $node[actualNodeName] ? $node[actualNodeName].json : `Nessun output specifico trovato per ${agentName}`;\n    }\n\n    // Tenta di analizzare il contenuto come JSON se l'agente \u00e8 un agente di revisione\n    if (agentName === 'Peer Reviewer' || agentName === 'Sensemaking Agent' || agentName === 'Prompt Engineer') {\n        try {\n            agentOutput = JSON.parse(rawContent);\n        } catch (e) {\n            console.warn(`Impossibile analizzare l'output di ${agentName} come JSON. Trattato come stringa.`);\n            agentOutput = rawContent;\n        }\n    } else {\n        agentOutput = rawContent;\n    }\n}\n\n// Registra il contributo\nconst contribution = {\n    agent: agentName,\n    output: agentOutput,\n    timestamp: new Date().toISOString()\n};\n\n// Assicurati che l'array dei contributi esista e aggiungi il nuovo contributo\nconst existingContributions = Array.isArray($json.contributions) ? $json.contributions : [];\n\n// Incrementa l'indice dell'agente per il loop (solo se non siamo in rielaborazione e questo non \u00e8 un agente di revisione che porta a rielaborazione)\n// Questo \u00e8 gestito dalla logica di 'Handle Redraft' che forza l'indice per il riavvio.\nif (!($json.redraftNeeded && $workflow.redraftLoopCount < 2 && agentName !== 'Synthesizer')) { // Evita doppio incremento se riavvia il Synthesizer\n    $workflow.currentAgentIndex += 1;\n}\n\n// Restituisce l'elemento aggiornato, preservando l'input originale, aggiungendo l'output corrente e tutti i contributi\nreturn [{ json: { ...$items[0].json, output: agentOutput, contributions: [...existingContributions, contribution] } }];"
      },
      "name": "Process Agent Output",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        400,
        208
      ],
      "id": "b7db08ff-4676-4a69-8b59-0f24788838f1"
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $env.SLACK_WEBHOOK_URL }}",
              "value2": true
            }
          ]
        }
      },
      "name": "Slack Enabled?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        -400,
        512
      ],
      "id": "e34f26d8-2fa6-462c-b103-6c6625935806"
    },
    {
      "parameters": {
        "text": "Pyragogy AI Village Workflow Completato!\nInput: {{ $json.body.input }}\nOutput Finale: {{ JSON.stringify($json.output) }}\nAgenti Eseguiti: {{ $workflow.agentSequence.join(', ') }}",
        "otherOptions": {},
        "attachments": []
      },
      "name": "Notify Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 1,
      "position": [
        -208,
        512
      ],
      "id": "0023b925-7e02-43d9-83bd-9a9ebee63cb8"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { finalOutput: $json.output, contributions: $json.contributions, agentSequence: $workflow.agentSequence } }}",
        "options": {}
      },
      "name": "Final Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        -400,
        400
      ],
      "id": "c04667b0-8fff-4fa4-8fc6-c9a106a4d687"
    }
  ],
  "connections": {
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Check DB Connection",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check DB Connection": {
      "main": [
        [
          {
            "node": "Meta-Orchestrator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Meta-Orchestrator": {
      "main": [
        [
          {
            "node": "Parse Orchestration Plan",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Orchestration Plan": {
      "main": [
        [
          {
            "node": "More Agents to Run?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "More Agents to Run?": {
      "main": [
        [
          {
            "node": "Prepare Agent Input",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack Enabled?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Agent Input": {
      "main": [
        [
          {
            "node": "Route Agents with Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route Agents with Switch": {
      "main": [
        [
          {
            "node": "Summarizer Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Summarizer Agent": {
      "main": [
        [
          {
            "node": "Process Agent Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Synthesizer Agent": {
      "main": [
        [
          {
            "node": "Process Agent Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Peer Reviewer Agent": {
      "main": [
        [
          {
            "node": "Sensemaking Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sensemaking Agent": {
      "main": [
        [
          {
            "node": "Prompt Engineer Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prompt Engineer Agent": {
      "main": [
        [
          {
            "node": "Evaluate Board Consensus",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Onboarding/Explainer Agent": {
      "main": [
        [
          {
            "node": "Process Agent Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add Handbook Metadata": {
      "main": [
        [
          {
            "node": "Generate Content for Review",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Content for Review": {
      "main": [
        [
          {
            "node": "Generate Review ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Review ID": {
      "main": [
        [
          {
            "node": "Send Review Request Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Review Request Email": {
      "main": [
        [
          {
            "node": "Wait for Human Approval",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait for Human Approval": {
      "main": [
        [
          {
            "node": "Human Decision Split",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Human Decision Split": {
      "main": [
        [
          {
            "node": "Save to handbook_entries",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Human Rejection",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save to handbook_entries": {
      "main": [
        [
          {
            "node": "Prepare Approved Contribution Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Approved Contribution Data": {
      "main": [
        [
          {
            "node": "Save Agent Contribution (Approved)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Agent Contribution (Approved)": {
      "main": [
        [
          {
            "node": "Generate GitHub File Path",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate GitHub File Path": {
      "main": [
        [
          {
            "node": "GitHub Enabled?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GitHub Enabled?": {
      "main": [
        [
          {
            "node": "Commit to GitHub (Approved)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Merge Archivist Paths",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Commit to GitHub (Approved)": {
      "main": [
        [
          {
            "node": "Merge Archivist Paths",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Human Rejection": {
      "main": [
        [
          {
            "node": "Merge Archivist Paths",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Archivist Paths": {
      "main": [
        [
          {
            "node": "Process Agent Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Evaluate Board Consensus": {
      "main": [
        [
          {
            "node": "Check Redraft Needed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Redraft Needed": {
      "main": [
        [
          {
            "node": "Handle Redraft",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Process Agent Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Handle Redraft": {
      "main": [
        [
          {
            "node": "More Agents to Run?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Agent Output": {
      "main": [
        [
          {
            "node": "More Agents to Run?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack Enabled?": {
      "main": [
        [
          {
            "node": "Notify Slack",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Final Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify Slack": {
      "main": [
        [
          {
            "node": "Final Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "meta": null,
  "versionId": "6280c054-718c-455a-901f-760d5ecfcaf1",
  "activeVersionId": null,
  "triggerCount": 0,
  "shared": [
    {
      "updatedAt": "2025-08-11T18:02:34.204Z",
      "createdAt": "2025-08-11T18:02:34.204Z",
      "role": "workflow:owner",
      "workflowId": "JGVj7vwvk7WvsGvP",
      "projectId": "HHopAZ4lOFgjhBzT"
    }
  ],
  "activeVersion": null,
  "tags": [
    {
      "updatedAt": "2025-08-11T18:00:37.173Z",
      "createdAt": "2025-08-11T18:00:37.173Z",
      "id": "FrTwNLPlDK1Iz254",
      "name": "Human-in-Loop"
    },
    {
      "updatedAt": "2025-08-11T18:00:37.179Z",
      "createdAt": "2025-08-11T18:00:37.179Z",
      "id": "Rmm17sRhdbs3zRxU",
      "name": "Orchestration"
    },
    {
      "updatedAt": "2025-08-11T18:00:37.119Z",
      "createdAt": "2025-08-11T18:00:37.119Z",
      "id": "VpIJNM77qNRE1hqp",
      "name": "Pyragogy"
    },
    {
      "updatedAt": "2025-08-11T18:00:37.108Z",
      "createdAt": "2025-08-11T18:00:37.108Z",
      "id": "WhZ0EMlhJQIwh2aa",
      "name": "Multi-Agent"
    }
  ]
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

My-Workflow-Jgvj7Vwvk7Wvsgvp. Uses start, postgres, openAi, emailSend. Webhook trigger; 35 nodes.

Source: https://github.com/abde0112/n8n_bkv2/blob/46910d507b6d39c2daa31e4c5789bf9da9457116/my-workflow-JGVj7vwvk7WvsGvP.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Pyragogy AI Village - Orchestrazione Master (Architettura Profonda V2). Uses start, postgres, openAi, emailSend. Webhook trigger; 37 nodes.

Start, Postgres, OpenAI +4
AI & RAG

Pyragogy AI Village - Orchestrazione Master (Architettura Profonda V2). Uses start, postgres, openAi, emailSend. Webhook trigger; 36 nodes.

Start, Postgres, OpenAI +4
AI & RAG

Pyragogy AI Village - Orchestrazione Master (Architettura Profonda V2). Uses start, postgres, openAi, emailSend. Webhook trigger; 35 nodes.

Start, Postgres, OpenAI +3
AI & RAG

Pyragogy AI Village - Orchestrazione Master (Architettura Profonda V2). Uses start, postgres, openAi, emailSend. Webhook trigger; 37 nodes.

Start, Postgres, OpenAI +4
AI & RAG

AI-Driven Handbook Generator with Multi-Agent Orchestration (Pyragogy AI Village). Uses start, postgres, openAi, emailSend. Webhook trigger; 36 nodes.

Start, Postgres, OpenAI +3