Document Generator

Describe the problem/error/question

Guys, I’m trying to use the AI - Groq to make a second-instance decision for a PROCON case - Consumer Protection Agency. The thing is, I have almost 50 nodes, there are 2 workflows and 2 sub-workflows. If I upload the first-instance process to the input folder so the node can go to that folder on the drive to get it, everything is working; it even generates the second-instance Decision (document, with name, complainant, defendant, case number, etc.), but it only gets some test data and not my process data. In WF-A it gets everything correctly, but when it goes to WF-B, it can’t retrieve the data. I’ve already tried everything, including clearing the cache, etc. What can I do?

What is the error message (if any)?

It doesn’t generate an error, it just doesn’t collect the data from the process, only from a test I did with fake data, and in the end it even generates a document with misaligned HTML and no data.

Please share your workflow

(Select the nodes on your canvas and use the keyboard shortcuts CMD+C/CTRL+C and CMD+V/CTRL+V to copy and paste the workflow.)

{
  "nodes": [
    {
      "parameters": {
        "driveId": "=My Drive",
        "folderId": "=1_uNvDvOn4gAeNaSFXF4wJ4C2zdvh_k4g",
        "title": "={{ $json.processo }}_Decisao_Provisoria"
      },
      "type": "n8n-nodes-base.googleDocs",
      "typeVersion": 2,
      "position": [
        832,
        -416
      ],
      "id": "ded62c5a-12e5-45bc-aff1-032af03bd5bb",
      "name": "5a. Criar Doc Google (Intermediário)",
      "notesInFlow": false,
      "alwaysOutputData": false,
      "executeOnce": false,
      "retryOnFail": false,
      "credentials": {
        "googleDocsOAuth2Api": {
          "id": "NVG2t05C4MGkjegx",
          "name": "Google Docs account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// NÓ 4: VERSÃO FINAL (Produção - Busca dados reais)\n\nconst results = [];\n\nfor (const item of items) {\n    try {\n        // 1. Pega o texto da IA (tenta várias possibilidades para garantir)\n        const rawDecisionText = \n            item.json.choices?.[0]?.message?.content || \n            item.json.content || \n            item.json.message?.content || \n            \"⚠️ Texto não gerado pela IA.\";\n\n        // 2. Recupera dados do processo (Lá do início, do Webhook/Validação)\n        // Usamos o Webhook como âncora segura\n        const dadosOriginais = $('Validação').first().json; \n        \n        // Fallback: Tenta pegar do item atual se o Webhook falhar\n        const processoNum = dadosOriginais.processo || item.json.processo || 'SEM NÚMERO';\n        const reclamante = dadosOriginais.reclamante || item.json.reclamante || 'Consumidor';\n\n        // 3. Trata quebras de linha para HTML\n        const textoFormatado = rawDecisionText.replace(/\\n/g, '<br>');\n\n        // 4. Monta o HTML\n        const htmlContent = `\n            <!DOCTYPE html>\n            <html>\n            <head>\n                <style>\n                    body { font-family: Arial, sans-serif; margin: 40px; line-height: 1.5; }\n                    .header { text-align: center; border-bottom: 2px solid #000; margin-bottom: 20px; }\n                    .info { margin-bottom: 20px; border: 1px solid #ccc; padding: 10px; background: #f9f9f9; }\n                </style>\n            </head>\n            <body>\n                <div class=\"header\">\n                    <h3>PROCURADORIA GERAL - DECISÃO</h3>\n                </div>\n                <div class=\"info\">\n                    <p><b>Processo:</b> ${processoNum}</p>\n                    <p><b>Reclamante:</b> ${reclamante}</p>\n                </div>\n                <hr>\n                ${textoFormatado}\n            </body>\n            </html>\n        `;\n\n        // 5. Retorna o pacote completo para os próximos nós\n        results.push({\n            json: {\n                ...dadosOriginais, // Mantém dados originais (hash, ids)\n                decision_html: htmlContent,\n                texto_ia_puro: rawDecisionText\n            }\n        });\n\n    } catch (error) {\n        // Em caso de erro, não trava o fluxo, mas avisa\n        results.push({ json: { error: error.message } });\n    }\n}\n\nreturn results;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        640,
        -352
      ],
      "id": "2701f821-edae-46ab-8a47-57fa68940987",
      "name": "4. Aplicar Formatação Institucional"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "4eb65c98-fe26-41b9-aa9d-a8864508e4eb",
              "leftValue": "={{ $json.processo }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            },
            {
              "id": "f52fb84c-eccc-4a31-ac0e-03aa5a94c550",
              "leftValue": "={{ $json.reclamante }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        144,
        -224
      ],
      "id": "01f48817-c01b-4bce-a498-28b9662a4daf",
      "name": "2. Validação Contrato A→B"
    },
    {
      "parameters": {
        "jsCode": "// NÓ 6: CORREÇÃO PARA GARANTIR BINÁRIO E DADOS\n\nconst crypto = require('crypto');\n\nfor (const item of items) {\n    // 1. Recupera o binário de forma segura (pode vir como 'data', 'file', ou 'document')\nconst binaryObject = item.binary;\n\n// Linha 7 (Protegida): SE não houver binário, lançamos um erro claro.\nif (!binaryObject) {\n   throw new Error(\"⚠️ Arquivo PDF Final não encontrado. O Nó 5b falhou em anexar o PDF.\");\n}\n\nconst binaryKey = Object.keys(binaryObject)[0];\nif (!binaryKey) throw new Error(\"Nenhuma chave binária encontrada.\");\n\nconst pdfFile = item.binary[binaryKey];\n\n    // 2. Calcula o Hash do PDF Final\n    const hashSum = crypto.createHash('sha256');\n    hashSum.update(Buffer.from(pdfFile.data, 'base64')); // Garante leitura correta do buffer n8n\n    const hashPdfFinal = hashSum.digest('hex');\n\n    // 3. Recupera dados originais (Processo e Hash Original)\n    // Como restauramos os dados no Nó 4, eles devem estar em item.json.\n    // Se não estiverem, usamos $('Webhook') novamente como fallback.\n    const processo = item.json.processo || $('Validação').first().json.processo;\n    const hashOriginal = item.json.hash_pdf_original || $('Validação').first().json.hash_pdf_original;\n\n    // 4. Consolida Log\n    const logData = {\n        id_execucao: $execution.id, \n        horario_registro: new Date().toISOString(),\n        versionamento_redator: \"V1.0.0\",\n        hash_pdf_original: hashOriginal,\n        hash_pdf_decisao: hashPdfFinal,\n        processo: processo,\n        status_final: \"SUCESSO\"\n    };\n\n    // 5. Prepara saída mantendo o binário para o próximo nó (Upload)\n    item.json.log_final = logData;\n    item.json.hash_pdf_decisao = hashPdfFinal;\n    item.json.processo = processo; // Garante que o nó de Upload do Drive tenha o nome do processo\n}\n\nreturn items;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1632,
        -320
      ],
      "id": "caf6088d-7d63-401d-a7ed-e765d00803f6",
      "name": "6. Logs e Hash Final (PDF)"
    },
    {
      "parameters": {
        "name": "={{ $json.processo }}_DECISAO_2GRAU_{{ DateTime.now().toFormat('yyyyMMdd') }}.pdf",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "folderId": {
          "__rl": true,
          "value": "1iw1O6cx9X-oLek55U-cTUIBypmMsqjpa",
          "mode": "id"
        },
        "options": {
          "useContentAsIndexableText": "=$binary.data"
        }
      },
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1824,
        -480
      ],
      "id": "eaf38ee2-bfad-405e-ba88-11ac00c30107",
      "name": "7b. Arquivar PDF Institucional",
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "xfACUJI7xp7A3Adj",
          "name": "Google Drive account 2"
        }
      }
    },
    {
      "parameters": {
        "name": "=Log_{{ $json.processo }}_{{ DateTime.now().toFormat('yyyyMMdd') }}.json",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "folderId": {
          "__rl": true,
          "value": "1Yrymti90JJn6MxmKbv01GocT7sKyWApM",
          "mode": "id"
        },
        "options": {
          "useContentAsIndexableText": "={{ JSON.stringify($json.log_final) }}"
        }
      },
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1824,
        -144
      ],
      "id": "0fade2d0-7e06-43a6-a969-84ded6dbc80c",
      "name": "7a. Registrar Log de Auditoria",
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "xfADCRU3PW7A3Adj",
          "name": "Google Drive account 2"
        }
      }
    },
    {
      "parameters": {
        "name": "=`FALHA_{{ $json.processo",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "folderId": {
          "__rl": true,
          "value": "=1Yrymti90JJn6MxmKbv01GocT7sKyWApM",
          "mode": "id"
        },
        "options": {
          "useContentAsIndexableText": "={{ JSON.stringify($json.log_erro) }}"
        }
      },
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        640,
        -80
      ],
      "id": "b982a933-f722-4f42-8458-8e731ca6c8d5",
      "name": "8b. Arquivar Falha (Auditoria)",
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "xfACPDQ7xp7A3Adj",
          "name": "Google Drive account 2"
        }
      }
    },
    {
      "parameters": {
        "fromEmail": "[email protected]",
        "toEmail": "[email protected]",
        "subject": "[ALERTA] FALHA DE VALIDAÇÃO PROCON-2G - Processo {{ $json.processo }}",
        "html": "=O Contrato A->B para o processo {{ $json.processo }} falhou na validação mínima. O log foi arquivado na pasta de Auditoria. Mensagem: {{ $json.log_erro.mensagem }}",
        "options": {}
      },
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        848,
        -80
      ],
      "id": "d84c151e-3c40-4fcb-8692-c3cd1d9dfbdd",
      "name": "Send email",
      "webhookId": "ac1951d8-5f61-4577-a545-b12121c428db",
      "credentials": {
        "smtp": {
          "id": "9WmdujNzJ495mSVe",
          "name": "SMTP account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Código para o Nó 8a (Gera Texto e Binário ao mesmo tempo)\n\n// 1. Monta o objeto de erro\nconst erroJSON = {\n    tipo: \"ERRO_VALIDACAO_B2\",\n    mensagem: \"JSON do Contrato A->B rejeitado: Campos essenciais ausentes.\",\n    horario_falha: new Date().toISOString(),\n    dados_originais: items[0].json // Salva o que veio errado para debug\n};\n\n// 2. Transforma em texto para salvar no arquivo\nconst erroString = JSON.stringify(erroJSON, null, 2);\n\n// 3. Retorna o JSON para o fluxo E cria o arquivo binário para o Drive\nreturn [{\n    json: {\n        log_erro: erroJSON \n    },\n    binary: {\n        data: {\n            data: Buffer.from(erroString).toString('base64'),\n            mimeType: 'application/json',\n            fileExtension: 'json',\n            fileName: `falha_validacao_${new Date().getTime()}.json`\n        }\n    }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        400,
        -80
      ],
      "id": "7b3b01eb-9b42-47ee-acf1-f882e99d53c7",
      "name": "Code in JavaScript"
    },
    {
      "parameters": {
        "workflowId": {
          "__rl": true,
          "value": "9977mlcvVVZmqkk9",
          "mode": "list",
          "cachedResultUrl": "/workflow/9977mlcvVVZmqkk9",
          "cachedResultName": "Sub-Workflow: Redação Groq"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {}
        },
        "mode": "each",
        "options": {}
      },
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1.3,
      "position": [
        400,
        -352
      ],
      "id": "aec54e87-6034-4a05-8ce0-812d9f068294",
      "name": "3. Redação Jurídica (Groq)"
    },
    {
      "parameters": {
        "jsCode": "// Código Cirúrgico para o Nó 'Validação'\nconst results = [];\n\nfor (const item of items) {\n  try {\n    const rootData = item.json.body || item.json;\n    \n    if (!rootData.choices || !rootData.choices[0]) {\n       throw new Error(\"Estrutura de resposta Groq não encontrada.\");\n    }\n\n    let rawContent = rootData.choices[0].message.content;\n    rawContent = rawContent.replace(/```json/g, \"\").replace(/```/g, \"\").trim();\n\n    const cleanData = JSON.parse(rawContent);\n    \n    // PEGA O HASH ORIGINAL, O ÚNICO DADO QUE PRECISAMOS DE BACKUP (assumindo que está no Webhook)\n    const hashOriginal = item.json.hash_pdf_original || \"HASH_NAO_ENCONTRADO\"; \n\n    // DEVOLVE APENAS O OBJETO LIMPO, ELIMINANDO O ITEM ANTERIOR\n    results.push({\n      json: {\n        ...cleanData, // Processo, Reclamante, etc. (DADOS LIMPOS)\n        hash_pdf_original: hashOriginal // Garante o hash original\n      }\n    });\n\n  } catch (error) {\n    // Se falhar, retorna um item com o erro, mas não trava o fluxo\n    results.push({ \n      json: { \n        erro_leitura: error.message,\n        processo: \"ERRO_PARSING\" \n      } \n    });\n  }\n}\n\nreturn results;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -64,
        -224
      ],
      "id": "605a1319-6934-4e8c-9790-4ec9744ae7c7",
      "name": "Validação"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "fa017607-94cb-4115-8547-ca9ddda8dda0",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -320,
        -224
      ],
      "id": "36505ef7-a91e-440e-a560-d7717d023a64",
      "name": "1. Entrada Contrato A-B",
      "webhookId": "fa017607-94cb-4115-8547-ca9ddda8dda0"
    },
    {
      "parameters": {
        "operation": "download",
        "fileId": {
          "__rl": true,
          "value": "={{ $('5a. Criar Doc Google (Intermediário)').first().json.id }}",
          "mode": "id"
        },
        "options": {
          "googleFileConversion": {
            "conversion": {
              "docsToFormat": "application/pdf"
            }
          }
        }
      },
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1408,
        -320
      ],
      "id": "b33d65dd-ca7b-4686-a4df-07f427626f39",
      "name": "5b. Exportar como PDF Final1",
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "fcwKCRU7xp7A3Adj",
          "name": "Google Drive account 2"
        }
      }
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        1200,
        -320
      ],
      "id": "d7aac819-9fd5-4ca8-b471-554aef89ce28",
      "name": "Wait",
      "webhookId": "d5757251-cc6d-4338-b199-49d1a537cac6"
    },
    {
      "parameters": {
        "operation": "update",
        "documentURL": "={{ $('5a. Criar Doc Google (Intermediário)').first().json.id }}",
        "actionsUi": {
          "actionFields": [
            {
              "action": "insert",
              "locationChoice": "location",
              "text": "={{ $('4. Aplicar Formatação Institucional').first().json.texto_ia_puro }}"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.googleDocs",
      "typeVersion": 2,
      "position": [
        1024,
        -432
      ],
      "id": "72b38a63-d966-4c32-8c38-ee74dba3b152",
      "name": "Update a document",
      "credentials": {
        "googleDocsOAuth2Api": {
          "id": "NMDEQ05C4MGkjegx",
          "name": "Google Docs account"
        }
      }
    }
  ],
  "connections": {
    "5a. Criar Doc Google (Intermediário)": {
      "main": [
        [
          {
            "node": "Update a document",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "4. Aplicar Formatação Institucional": {
      "main": [
        [
          {
            "node": "5a. Criar Doc Google (Intermediário)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2. Validação Contrato A→B": {
      "main": [
        [
          {
            "node": "3. Redação Jurídica (Groq)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "6. Logs e Hash Final (PDF)": {
      "main": [
        [
          {
            "node": "7a. Registrar Log de Auditoria",
            "type": "main",
            "index": 0
          },
          {
            "node": "7b. Arquivar PDF Institucional",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "8b. Arquivar Falha (Auditoria)": {
      "main": [
        [
          {
            "node": "Send email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "8b. Arquivar Falha (Auditoria)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3. Redação Jurídica (Groq)": {
      "main": [
        [
          {
            "node": "4. Aplicar Formatação Institucional",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validação": {
      "main": [
        [
          {
            "node": "2. Validação Contrato A→B",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1. Entrada Contrato A-B": {
      "main": [
        [
          {
            "node": "Validação",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "5b. Exportar como PDF Final1": {
      "main": [
        [
          {
            "node": "6. Logs e Hash Final (PDF)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "5b. Exportar como PDF Final1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update a document": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "3665290dee83316fb2090d0c18e97256d7ffe7d9232e83e28dbc0e022948d141"
  }
}

Share the output returned by the last node

Information on your n8n setup

  • n8n version: 1.121.3
  • Database (default: SQLite):
  • n8n EXECUTIONS_PROCESS setting (default: own, main): own
  • Running n8n via (Docker, npm, n8n cloud, desktop app): desktop app
  • Operating system: Windows 11 Pro versão 23H2

Hello @Mario_Lioli_Pacheco, welcome to the community! :n8n:

Do you use any (waiting for webhook, forms, HITL, etc.) in the sub-workflow?

If yes, then this behavior is expected in n8n v1, but it has been changed in the upcoming v2.

Please read more here:

If no, then there should be a logic that needs to be fixed in your workflow..

1 Like

Good afternoon, I updated the n8n version, but it didn’t solve my problem. Now it gives an error when it reaches the node to go to the Groq website and bring a valid model. Could you read my question again and help me? I would be very grateful.