AutomationFlowsAI & RAG › Screen and Triage Job Applications with Ollama, Gmail, and Google Sheets

Screen and Triage Job Applications with Ollama, Gmail, and Google Sheets

ByOriol Seguí @oxsr11 on n8n.io

This workflow collects job applications through an n8n form, extracts text from the uploaded PDF CV, analyzes it with Ollama, and records the results in Google Sheets while using Gmail to send candidate confirmations, route high-fit candidates to human approval, and email alerts…

Event trigger★★★★☆ complexityAI-powered18 nodesGmailGoogle SheetsForm TriggerOllama
AI & RAG Trigger: Event Nodes: 18 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #17433 — we link there as the canonical source.

This workflow follows the Form Trigger → Gmail 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
{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "9271f4b1-23d4-4f17-8e6a-af7caad6eb78",
      "name": "Extract from File",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        -176,
        -48
      ],
      "parameters": {
        "options": {},
        "operation": "pdf",
        "binaryPropertyName": "={{ $('Application Reception').item.binary.Curr_culum }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "d2b87f22-73f1-4efa-97bc-4fa84efa2504",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        -624,
        192
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "24fd8850-53a0-407f-8c3b-86d17f00d6d6",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.nivel_ajuste }}",
              "rightValue": "Alto"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "44c473c7-d494-4934-863e-b4d3d96aff23",
      "name": "Clear data",
      "type": "n8n-nodes-base.code",
      "position": [
        -832,
        192
      ],
      "parameters": {
        "jsCode": "// ============================================================\n// 1. OBTENER LA RESPUESTA DE OLLAMA\n// ============================================================\n\nconst rawContent =\n  $json.content ??\n  $json.message?.content ??\n  $json.response ??\n  $json.output ??\n  '';\n\nif (\n  rawContent === null ||\n  rawContent === undefined ||\n  (\n    typeof rawContent === 'string' &&\n    !rawContent.trim()\n  )\n) {\n  throw new Error(\n    'No se encontr\u00f3 contenido v\u00e1lido en la respuesta de Ollama.'\n  );\n}\n\n\n// ============================================================\n// 2. FUNCIONES PARA RECUPERAR Y REPARAR EL JSON\n// ============================================================\n\n// Extrae el primer objeto o array JSON completo.\n// Permite ignorar textos como:\n// \"Aqu\u00ed tienes el resultado: { ... }\"\nconst extractFirstJsonStructure = (text) => {\n  const objectPosition = text.indexOf('{');\n  const arrayPosition = text.indexOf('[');\n\n  const possibleStarts = [\n    objectPosition,\n    arrayPosition,\n  ].filter((position) => position >= 0);\n\n  if (possibleStarts.length === 0) {\n    return text.trim();\n  }\n\n  const startPosition = Math.min(...possibleStarts);\n\n  const stack = [];\n  let insideString = false;\n  let escaped = false;\n\n  for (\n    let index = startPosition;\n    index < text.length;\n    index++\n  ) {\n    const character = text[index];\n\n    if (insideString) {\n      if (escaped) {\n        escaped = false;\n        continue;\n      }\n\n      if (character === '\\\\') {\n        escaped = true;\n        continue;\n      }\n\n      if (character === '\"') {\n        insideString = false;\n      }\n\n      continue;\n    }\n\n    if (character === '\"') {\n      insideString = true;\n      continue;\n    }\n\n    if (\n      character === '{' ||\n      character === '['\n    ) {\n      stack.push(character);\n      continue;\n    }\n\n    if (\n      character === '}' ||\n      character === ']'\n    ) {\n      const expectedOpening =\n        character === '}' ? '{' : '[';\n\n      const lastOpening =\n        stack[stack.length - 1];\n\n      if (lastOpening !== expectedOpening) {\n        continue;\n      }\n\n      stack.pop();\n\n      if (stack.length === 0) {\n        return text\n          .slice(startPosition, index + 1)\n          .trim();\n      }\n    }\n  }\n\n  // Si no encuentra un cierre completo,\n  // devuelve desde el primer inicio de JSON.\n  return text.slice(startPosition).trim();\n};\n\n\n// Elimina Markdown, caracteres invisibles y texto externo.\nconst prepareJsonText = (value) => {\n  let text = String(value)\n    // Elimina BOM.\n    .replace(/^\\uFEFF/, '')\n\n    // Elimina caracteres invisibles frecuentes.\n    .replace(/[\\u200B-\\u200D\\u2060]/g, '')\n\n    // Normaliza comillas dobles tipogr\u00e1ficas.\n    .replace(/[\u201c\u201d]/g, '\"')\n\n    .trim();\n\n  // Elimina bloques Markdown.\n  text = text\n    .replace(/^```(?:json)?\\s*/i, '')\n    .replace(/\\s*```$/i, '')\n    .trim();\n\n  return extractFirstJsonStructure(text);\n};\n\n\n// Devuelve el \u00faltimo car\u00e1cter no vac\u00edo ya generado.\nconst getPreviousSignificantCharacter = (text) => {\n  for (\n    let index = text.length - 1;\n    index >= 0;\n    index--\n  ) {\n    if (!/\\s/.test(text[index])) {\n      return text[index];\n    }\n  }\n\n  return '';\n};\n\n\n// Corrige errores estructurales frecuentes de los modelos:\n// - \"Elemento\"\n// * \"Elemento\"\n// + \"Elemento\"\n// Comas finales: [\"a\", \"b\",]\n// Saltos de l\u00ednea sin escapar dentro de textos.\nconst repairJsonStructure = (text) => {\n  let repairedText = '';\n  let insideString = false;\n  let escaped = false;\n\n  for (\n    let index = 0;\n    index < text.length;\n    index++\n  ) {\n    const character = text[index];\n\n    if (insideString) {\n      if (escaped) {\n        repairedText += character;\n        escaped = false;\n        continue;\n      }\n\n      if (character === '\\\\') {\n        repairedText += character;\n        escaped = true;\n        continue;\n      }\n\n      if (character === '\"') {\n        repairedText += character;\n        insideString = false;\n        continue;\n      }\n\n      // Los saltos de l\u00ednea literales no son v\u00e1lidos\n      // dentro de strings JSON.\n      if (\n        character === '\\n' ||\n        character === '\\r' ||\n        character === '\\t'\n      ) {\n        repairedText += ' ';\n        continue;\n      }\n\n      repairedText += character;\n      continue;\n    }\n\n    if (character === '\"') {\n      repairedText += character;\n      insideString = true;\n      continue;\n    }\n\n    // Corrige listas Markdown introducidas dentro de arrays.\n    if (\n      character === '-' ||\n      character === '*' ||\n      character === '+'\n    ) {\n      const previousCharacter =\n        getPreviousSignificantCharacter(repairedText);\n\n      let nextIndex = index + 1;\n\n      while (\n        nextIndex < text.length &&\n        /\\s/.test(text[nextIndex])\n      ) {\n        nextIndex++;\n      }\n\n      const nextCharacter = text[nextIndex];\n\n      const isStructuralBullet =\n        (\n          previousCharacter === '[' ||\n          previousCharacter === ',' ||\n          previousCharacter === '{'\n        ) &&\n        (\n          nextCharacter === '\"' ||\n          nextCharacter === '{' ||\n          nextCharacter === '['\n        );\n\n      if (isStructuralBullet) {\n        // Omite el s\u00edmbolo -, * o +.\n        // Tambi\u00e9n avanza hasta el siguiente valor real.\n        index = nextIndex - 1;\n        continue;\n      }\n    }\n\n    // Elimina comas finales antes de } o ].\n    if (character === ',') {\n      let nextIndex = index + 1;\n\n      while (\n        nextIndex < text.length &&\n        /\\s/.test(text[nextIndex])\n      ) {\n        nextIndex++;\n      }\n\n      const nextCharacter = text[nextIndex];\n\n      if (\n        nextCharacter === '}' ||\n        nextCharacter === ']'\n      ) {\n        continue;\n      }\n    }\n\n    repairedText += character;\n  }\n\n  return repairedText.trim();\n};\n\n\n// Convierte literales habituales de Python/JavaScript\n// a valores v\u00e1lidos de JSON.\n// Solo act\u00faa fuera de strings.\nconst repairBareLiterals = (text) => {\n  const replacements = {\n    True: 'true',\n    False: 'false',\n    None: 'null',\n    undefined: 'null',\n    NaN: 'null',\n  };\n\n  let repairedText = '';\n  let insideString = false;\n  let escaped = false;\n\n  for (\n    let index = 0;\n    index < text.length;\n    index++\n  ) {\n    const character = text[index];\n\n    if (insideString) {\n      repairedText += character;\n\n      if (escaped) {\n        escaped = false;\n        continue;\n      }\n\n      if (character === '\\\\') {\n        escaped = true;\n        continue;\n      }\n\n      if (character === '\"') {\n        insideString = false;\n      }\n\n      continue;\n    }\n\n    if (character === '\"') {\n      repairedText += character;\n      insideString = true;\n      continue;\n    }\n\n    if (/[A-Za-z]/.test(character)) {\n      let endIndex = index;\n\n      while (\n        endIndex < text.length &&\n        /[A-Za-z]/.test(text[endIndex])\n      ) {\n        endIndex++;\n      }\n\n      const word = text.slice(index, endIndex);\n\n      repairedText +=\n        replacements[word] ?? word;\n\n      index = endIndex - 1;\n      continue;\n    }\n\n    repairedText += character;\n  }\n\n  return repairedText;\n};\n\n\n// Limita el contenido mostrado en los errores,\n// para evitar respuestas excesivamente grandes.\nconst getErrorPreview = (\n  value,\n  maximumLength = 7000\n) => {\n  const text = String(value);\n\n  if (text.length <= maximumLength) {\n    return text;\n  }\n\n  return `${text.slice(0, maximumLength)}... [contenido recortado]`;\n};\n\n\n// ============================================================\n// 3. INTERPRETAR EL JSON\n// ============================================================\n\nlet analysis;\nlet cleanedContent = '';\nlet repairedContent = '';\n\n// Algunas integraciones pueden devolver el objeto directamente.\nif (\n  typeof rawContent === 'object' &&\n  rawContent !== null &&\n  !Array.isArray(rawContent)\n) {\n  analysis = rawContent;\n} else {\n  cleanedContent = prepareJsonText(rawContent);\n\n  try {\n    // Primer intento: JSON exacto, sin modificarlo.\n    analysis = JSON.parse(cleanedContent);\n  } catch (originalError) {\n    // Segundo intento: reparar errores frecuentes.\n    repairedContent = repairBareLiterals(\n      repairJsonStructure(cleanedContent)\n    );\n\n    try {\n      analysis = JSON.parse(repairedContent);\n    } catch (repairedError) {\n      throw new Error(\n        [\n          'Ollama no devolvi\u00f3 JSON v\u00e1lido.',\n          `Error original: ${originalError.message}.`,\n          `Error despu\u00e9s de intentar repararlo: ${repairedError.message}.`,\n          '',\n          'Contenido reparado recibido:',\n          getErrorPreview(repairedContent),\n        ].join(' ')\n      );\n    }\n  }\n}\n\nif (\n  typeof analysis !== 'object' ||\n  analysis === null ||\n  Array.isArray(analysis)\n) {\n  throw new Error(\n    'La respuesta de Ollama debe ser un objeto JSON.'\n  );\n}\n\n\n// ============================================================\n// 4. FUNCIONES DE NORMALIZACI\u00d3N\n// ============================================================\n\n// Limpia un texto:\n// - Elimina saltos de l\u00ednea.\n// - Elimina tabulaciones.\n// - Reduce espacios repetidos.\n// - Quita espacios iniciales y finales.\nconst cleanString = (value, fallback = '') => {\n  if (\n    value === null ||\n    value === undefined\n  ) {\n    return fallback;\n  }\n\n  return String(value)\n    .replace(/[\\r\\n\\t]+/g, ' ')\n    .replace(/\\s+/g, ' ')\n    .trim();\n};\n\n\n// Normaliza un n\u00famero dentro de unos l\u00edmites.\nconst cleanNumber = (\n  value,\n  min = 0,\n  max = 100\n) => {\n  let normalizedValue = value;\n\n  // Permite valores como:\n  // \"35\"\n  // \"35 puntos\"\n  // \"35/40\"\n  // \"35,5\"\n  if (typeof value === 'string') {\n    const numberMatch = value\n      .replace(',', '.')\n      .match(/-?\\d+(?:\\.\\d+)?/);\n\n    normalizedValue =\n      numberMatch?.[0] ?? value;\n  }\n\n  const number = Number(normalizedValue);\n\n  if (!Number.isFinite(number)) {\n    return min;\n  }\n\n  return Math.min(\n    max,\n    Math.max(min, Math.round(number))\n  );\n};\n\n\n// Normaliza valores booleanos.\nconst cleanBoolean = (\n  value,\n  fallback = true\n) => {\n  if (typeof value === 'boolean') {\n    return value;\n  }\n\n  if (typeof value === 'number') {\n    if (value === 1) {\n      return true;\n    }\n\n    if (value === 0) {\n      return false;\n    }\n  }\n\n  if (typeof value === 'string') {\n    const normalizedValue = value\n      .trim()\n      .toLowerCase();\n\n    if (\n      normalizedValue === 'true' ||\n      normalizedValue === 's\u00ed' ||\n      normalizedValue === 'si' ||\n      normalizedValue === '1'\n    ) {\n      return true;\n    }\n\n    if (\n      normalizedValue === 'false' ||\n      normalizedValue === 'no' ||\n      normalizedValue === '0'\n    ) {\n      return false;\n    }\n  }\n\n  return fallback;\n};\n\n\n// Limpia los elementos de un array.\nconst cleanArray = (value) => {\n  if (!Array.isArray(value)) {\n    return [];\n  }\n\n  return value\n    .map((item) => {\n      if (\n        typeof item === 'object' &&\n        item !== null\n      ) {\n        return cleanString(\n          item.nombre ??\n          item.valor ??\n          item.texto ??\n          item.descripcion ??\n          ''\n        );\n      }\n\n      return cleanString(item);\n    })\n    .filter(Boolean);\n};\n\n\n// Convierte un array en frases separadas por puntos.\nconst arrayToText = (value) => {\n  const items = cleanArray(value);\n\n  if (items.length === 0) {\n    return '';\n  }\n\n  const normalizedItems = items.map((item) =>\n    item\n      .replace(/[.;,\\s]+$/g, '')\n      .trim()\n  );\n\n  return `${normalizedItems.join('. ')}.`;\n};\n\n\n// Convierte un array en texto separado por comas.\nconst arrayToCommaText = (value) => {\n  return cleanArray(value).join(', ');\n};\n\n\n// ============================================================\n// 5. NORMALIZAR PUNTUACIONES\n// ============================================================\n\nconst puntuacionTecnica = cleanNumber(\n  analysis.puntuacion_tecnica,\n  0,\n  40\n);\n\nconst puntuacionExperiencia = cleanNumber(\n  analysis.puntuacion_experiencia,\n  0,\n  25\n);\n\nconst puntuacionFormacion = cleanNumber(\n  analysis.puntuacion_formacion,\n  0,\n  15\n);\n\nconst puntuacionProyectos = cleanNumber(\n  analysis.puntuacion_proyectos,\n  0,\n  10\n);\n\nconst puntuacionComplementaria = cleanNumber(\n  analysis.puntuacion_complementaria,\n  0,\n  10\n);\n\n\n// Calcula la puntuaci\u00f3n total para evitar\n// inconsistencias producidas por la IA.\nconst puntuacionCalculada =\n  puntuacionTecnica +\n  puntuacionExperiencia +\n  puntuacionFormacion +\n  puntuacionProyectos +\n  puntuacionComplementaria;\n\n\n// ============================================================\n// 6. NORMALIZAR NIVEL DE AJUSTE\n// ============================================================\n\nlet nivelAjuste = cleanString(\n  analysis.nivel_ajuste\n);\n\nconst nivelAjusteNormalizado =\n  nivelAjuste.toLowerCase();\n\nif (nivelAjusteNormalizado === 'alto') {\n  nivelAjuste = 'Alto';\n} else if (nivelAjusteNormalizado === 'medio') {\n  nivelAjuste = 'Medio';\n} else if (nivelAjusteNormalizado === 'bajo') {\n  nivelAjuste = 'Bajo';\n} else if (puntuacionCalculada >= 75) {\n  nivelAjuste = 'Alto';\n} else if (puntuacionCalculada >= 50) {\n  nivelAjuste = 'Medio';\n} else {\n  nivelAjuste = 'Bajo';\n}\n\n\n// ============================================================\n// 7. NORMALIZAR RECOMENDACI\u00d3N\n// ============================================================\n\nlet recomendacion = cleanString(\n  analysis.recomendacion\n);\n\nconst recomendacionNormalizada =\n  recomendacion.toLowerCase();\n\nif (\n  recomendacionNormalizada ===\n  'revisi\u00f3n prioritaria'\n) {\n  recomendacion = 'Revisi\u00f3n prioritaria';\n} else if (\n  recomendacionNormalizada ===\n  'revisi\u00f3n est\u00e1ndar'\n) {\n  recomendacion = 'Revisi\u00f3n est\u00e1ndar';\n} else if (\n  recomendacionNormalizada ===\n  'requiere m\u00e1s informaci\u00f3n'\n) {\n  recomendacion = 'Requiere m\u00e1s informaci\u00f3n';\n} else if (puntuacionCalculada >= 75) {\n  recomendacion = 'Revisi\u00f3n prioritaria';\n} else if (puntuacionCalculada >= 50) {\n  recomendacion = 'Revisi\u00f3n est\u00e1ndar';\n} else {\n  recomendacion = 'Requiere m\u00e1s informaci\u00f3n';\n}\n\n\n// ============================================================\n// 8. NORMALIZAR IDIOMAS\n// ============================================================\n\nconst idiomas = Array.isArray(analysis.idiomas)\n  ? analysis.idiomas\n      .map((idioma) => {\n        // Permite idiomas como objeto:\n        // { \"idioma\": \"Ingl\u00e9s\", \"nivel\": \"A1\" }\n        if (\n          typeof idioma === 'object' &&\n          idioma !== null\n        ) {\n          return {\n            idioma: cleanString(\n              idioma.idioma ??\n              idioma.nombre\n            ),\n            nivel: cleanString(\n              idioma.nivel,\n              'No especificado'\n            ),\n          };\n        }\n\n        // Tambi\u00e9n acepta un string simple.\n        return {\n          idioma: cleanString(idioma),\n          nivel: 'No especificado',\n        };\n      })\n      .filter((idioma) => idioma.idioma)\n  : [];\n\n\n// Idiomas sin saltos de l\u00ednea.\nconst idiomasTexto = idiomas\n  .map(\n    (item) =>\n      `${item.idioma}: ${item.nivel}`\n  )\n  .join(' | ');\n\n\n// ============================================================\n// 9. OBJETO FINAL PARA GOOGLE SHEETS,\n//    POWER AUTOMATE O SHAREPOINT\n// ============================================================\n\nconst result = {\n  procesado_correctamente: true,\n\n  resumen_profesional: cleanString(\n    analysis.resumen_profesional\n  ),\n\n  puntuacion_total: puntuacionCalculada,\n  puntuacion_tecnica: puntuacionTecnica,\n  puntuacion_experiencia:\n    puntuacionExperiencia,\n  puntuacion_formacion:\n    puntuacionFormacion,\n  puntuacion_proyectos:\n    puntuacionProyectos,\n  puntuacion_complementaria:\n    puntuacionComplementaria,\n\n  nivel_ajuste: nivelAjuste,\n  recomendacion,\n\n  justificacion_recomendacion: cleanString(\n    analysis.justificacion_recomendacion\n  ),\n\n  anos_experiencia_confirmados: cleanNumber(\n    analysis.anos_experiencia_confirmados,\n    0,\n    50\n  ),\n\n  experiencia_relevante: cleanString(\n    analysis.experiencia_relevante\n  ),\n\n  habilidades_confirmadas: arrayToCommaText(\n    analysis.habilidades_confirmadas\n  ),\n\n  herramientas_automatizacion:\n    arrayToCommaText(\n      analysis.herramientas_automatizacion\n    ),\n\n  lenguajes_programacion: arrayToCommaText(\n    analysis.lenguajes_programacion\n  ),\n\n  infraestructura_y_sistemas:\n    arrayToCommaText(\n      analysis.infraestructura_y_sistemas\n    ),\n\n  formacion_relevante: arrayToText(\n    analysis.formacion_relevante\n  ),\n\n  idiomas: idiomasTexto,\n\n  requisitos_cumplidos: arrayToText(\n    analysis.requisitos_cumplidos\n  ),\n\n  requisitos_no_especificados: arrayToText(\n    analysis.requisitos_no_especificados\n  ),\n\n  aspectos_destacados: arrayToText(\n    analysis.aspectos_destacados\n  ),\n\n  aspectos_a_verificar: arrayToText(\n    analysis.aspectos_a_verificar\n  ),\n\n  preguntas_entrevista: arrayToText(\n    analysis.preguntas_entrevista\n  ),\n\n  requiere_revision_humana: cleanBoolean(\n    analysis.requiere_revision_humana,\n    true\n  ),\n\n  // Copia estructurada para usos posteriores.\n  detalle_json: JSON.stringify({\n    habilidades_confirmadas: cleanArray(\n      analysis.habilidades_confirmadas\n    ),\n\n    herramientas_automatizacion: cleanArray(\n      analysis.herramientas_automatizacion\n    ),\n\n    lenguajes_programacion: cleanArray(\n      analysis.lenguajes_programacion\n    ),\n\n    infraestructura_y_sistemas: cleanArray(\n      analysis.infraestructura_y_sistemas\n    ),\n\n    formacion_relevante: cleanArray(\n      analysis.formacion_relevante\n    ),\n\n    idiomas,\n\n    requisitos_cumplidos: cleanArray(\n      analysis.requisitos_cumplidos\n    ),\n\n    requisitos_no_especificados: cleanArray(\n      analysis.requisitos_no_especificados\n    ),\n\n    aspectos_destacados: cleanArray(\n      analysis.aspectos_destacados\n    ),\n\n    aspectos_a_verificar: cleanArray(\n      analysis.aspectos_a_verificar\n    ),\n\n    preguntas_entrevista: cleanArray(\n      analysis.preguntas_entrevista\n    ),\n  }),\n};\n\n\nreturn [\n  {\n    json: result,\n  },\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "188225b9-4278-441c-9b62-1c134716ba18",
      "name": "Error model o tokens",
      "type": "n8n-nodes-base.gmail",
      "position": [
        160,
        -208
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "faltan creditos ",
        "options": {},
        "subject": "Ollama"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.2
    },
    {
      "id": "6f4dca49-a2f8-4ff4-a55d-582e218a10a0",
      "name": "Append row in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -400,
        192
      ],
      "parameters": {
        "columns": {
          "value": {
            "Email": "={{ $('Application Reception').item.json['Correo electr\u00f3nico'] }}",
            "Phone": "={{ $('Application Reception').item.json['Tel\u00e9fono'] }}",
            "Estado": "={{ null }}",
            "idiomas": "={{ $json.idiomas }}",
            "detalle_json": "={{ $json.detalle_json }}",
            "nivel_ajuste": "={{ $json.nivel_ajuste }}",
            "recomendacion": "={{ $json.recomendacion }}",
            "Nombre competo": "={{ $('Application Reception').item.json.Nombre }} {{ $('Application Reception').item.json.Apellidos }}",
            "puntuacion_total": "={{ $json.puntuacion_total }}",
            "puntuacion_tecnica": "={{ $json.puntuacion_tecnica }}",
            "aspectos_destacados": "={{ $json.aspectos_destacados }}",
            "formacion_relevante": "={{ $json.formacion_relevante }}",
            "resumen_profesional": "={{ $json.resumen_profesional }}",
            "aspectos_a_verificar": "={{ $json.aspectos_a_verificar }}",
            "preguntas_entrevista": "={{ $json.preguntas_entrevista }}",
            "puntuacion_formacion": "={{ $json.puntuacion_formacion }}",
            "puntuacion_proyectos": "={{ $json.puntuacion_proyectos }}",
            "requisitos_cumplidos": "={{ $json.requisitos_cumplidos }}",
            "experiencia_relevante": "={{ $json.experiencia_relevante }}",
            "lenguajes_programacion": "={{ $json.lenguajes_programacion }}",
            "puntuacion_experiencia": "={{ $json.puntuacion_experiencia }}",
            "habilidades_confirmadas": "={{ $json.habilidades_confirmadas }}",
            "procesado_correctamente": "={{ $json.procesado_correctamente }}",
            "requiere_revision_humana": "={{ $json.requiere_revision_humana }}",
            "puntuacion_complementaria": "={{ $json.puntuacion_complementaria }}",
            "infraestructura_y_sistemas": "={{ $json.infraestructura_y_sistemas }}",
            "herramientas_automatizacion": "={{ $json.herramientas_automatizacion }}",
            "justificacion_recomendacion": "={{ $json.justificacion_recomendacion }}",
            "requisitos_no_especificados": "={{ $json.requisitos_no_especificados }}",
            "anos_experiencia_confirmados": "={{ $json.anos_experiencia_confirmados }}"
          },
          "schema": [
            {
              "id": "procesado_correctamente",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "procesado_correctamente",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Estado",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Estado",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Nombre competo",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Nombre competo",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Phone",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "resumen_profesional",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "resumen_profesional",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "puntuacion_total",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "puntuacion_total",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "puntuacion_tecnica",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "puntuacion_tecnica",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "puntuacion_experiencia",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "puntuacion_experiencia",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "puntuacion_formacion",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "puntuacion_formacion",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "puntuacion_proyectos",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "puntuacion_proyectos",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "puntuacion_complementaria",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "puntuacion_complementaria",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "nivel_ajuste",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "nivel_ajuste",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "recomendacion",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "recomendacion",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "justificacion_recomendacion",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "justificacion_recomendacion",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "anos_experiencia_confirmados",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "anos_experiencia_confirmados",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "experiencia_relevante",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "experiencia_relevante",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "habilidades_confirmadas",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "habilidades_confirmadas",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "herramientas_automatizacion",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "herramientas_automatizacion",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lenguajes_programacion",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lenguajes_programacion",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "infraestructura_y_sistemas",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "infraestructura_y_sistemas",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "formacion_relevante",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "formacion_relevante",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "idiomas",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "idiomas",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "requisitos_cumplidos",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "requisitos_cumplidos",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "requisitos_no_especificados",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "requisitos_no_especificados",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "aspectos_destacados",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "aspectos_destacados",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "aspectos_a_verificar",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "aspectos_a_verificar",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "preguntas_entrevista",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "preguntas_entrevista",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "requiere_revision_humana",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "requiere_revision_humana",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "detalle_json",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "detalle_json",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1610898109,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/10ldNybxk6T7yK4HWgsMTHmrwQEntyGWCdGRTxBsCkLA/edit#gid=1610898109",
          "cachedResultName": "Curr\u00edculums"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "10ldNybxk6T7yK4HWgsMTHmrwQEntyGWCdGRTxBsCkLA",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/10ldNybxk6T7yK4HWgsMTHmrwQEntyGWCdGRTxBsCkLA/edit?usp=drivesdk",
          "cachedResultName": "Curr\u00edculums"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.7,
      "waitBetweenTries": 5000
    },
    {
      "id": "e1ed01e9-53ff-4cc3-9203-319beabb0393",
      "name": "Update row in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        32,
        192
      ],
      "parameters": {
        "columns": {
          "value": {
            "Estado": "={{ $json.data.approved }}",
            "justificacion_recomendacion": "={{ $('Clear data').item.json.justificacion_recomendacion }}"
          },
          "schema": [
            {
              "id": "procesado_correctamente",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "procesado_correctamente",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Estado",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Estado",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Nombre competo",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Nombre competo",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "resumen_profesional",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "resumen_profesional",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "puntuacion_total",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "puntuacion_total",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "puntuacion_tecnica",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "puntuacion_tecnica",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "puntuacion_experiencia",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "puntuacion_experiencia",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "puntuacion_formacion",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "puntuacion_formacion",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "puntuacion_proyectos",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "puntuacion_proyectos",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "puntuacion_complementaria",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "puntuacion_complementaria",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "nivel_ajuste",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "nivel_ajuste",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "recomendacion",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "recomendacion",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "justificacion_recomendacion",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "justificacion_recomendacion",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "anos_experiencia_confirmados",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "anos_experiencia_confirmados",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "experiencia_relevante",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "experiencia_relevante",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "habilidades_confirmadas",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "habilidades_confirmadas",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "herramientas_automatizacion",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "herramientas_automatizacion",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lenguajes_programacion",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "lenguajes_programacion",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "infraestructura_y_sistemas",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "infraestructura_y_sistemas",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "formacion_relevante",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "formacion_relevante",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "idiomas",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "idiomas",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "requisitos_cumplidos",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "requisitos_cumplidos",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "requisitos_no_especificados",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "requisitos_no_especificados",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "aspectos_destacados",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "aspectos_destacados",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "aspectos_a_verificar",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "aspectos_a_verificar",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "preguntas_entrevista",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "preguntas_entrevista",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "requiere_revision_humana",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "requiere_revision_humana",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "detalle_json",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "detalle_json",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "justificacion_recomendacion"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1610898109,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/10ldNybxk6T7yK4HWgsMTHmrwQEntyGWCdGRTxBsCkLA/edit#gid=1610898109",
          "cachedResultName": "Curr\u00edculums"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "10ldNybxk6T7yK4HWgsMTHmrwQEntyGWCdGRTxBsCkLA",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/10ldNybxk6T7yK4HWgsMTHmrwQEntyGWCdGRTxBsCkLA/edit?usp=drivesdk",
          "cachedResultName": "Curr\u00edculums"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.7,
      "waitBetweenTries": 5000
    },
    {
      "id": "4d1cae0e-e7aa-49fd-9ddf-9d2e046affbf",
      "name": "Human review",
      "type": "n8n-nodes-base.gmail",
      "maxTries": 5,
      "position": [
        -176,
        192
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=Resumen profesional: \n\n{{ $json.resumen_profesional }}\n\nFormaci\u00f3n revelante:\n\n{{ $json.formacion_relevante }}\n\nJustificaci\u00f3n: \n\n{{ $json.justificacion_recomendacion }}\n\nPuntuaci\u00f3n total: {{ $json.puntuacion_total }}\nPuntuaci\u00f3n t\u00e9cnica: {{ $json.puntuacion_tecnica }}\nPuntuaci\u00f3n formaci\u00f3n: {{ $json.puntuacion_formacion }}\nPuntuaci\u00f3n experiencia: {{ $json.puntuacion_experiencia }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "Candidatura",
        "operation": "sendAndWait",
        "approvalOptions": {
          "values": {
            "approvalType": "double",
            "approveLabel": "Aprobar CV",
            "disapproveLabel": "Rechazar CV"
          }
        }
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.2,
      "waitBetweenTries": 5000
    },
    {
      "id": "a5cc5909-7f15-404a-8c4f-e14eee4cd0a8",
      "name": "OPTIONS",
      "type": "n8n-nodes-base.set",
      "position": [
        -624,
        -48
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "c23b5870-0455-4f1a-8b26-360a6bebe4e7",
              "name": "mail",
              "type": "string",
              "value": "user@example.com"
            },
            {
              "id": "1ef0568e-4466-48ae-9f70-d93f4b78f056",
              "name": "business info",
              "type": "string",
              "value": "=CLCode es una empresa que lleva a\u00f1os en el mund ode la utomatizaci\u00f3n, buscamos un t\u00e9cnico especializado en eso, que viva cerca, las condiciones son esas: \n\nPuesto: Especialista IA\nOficinas: Vic, Catalu\u00f1a. \nModalidad: 1 - 2 d\u00edas de teletrabajo a la semana\nHorario: flexibilidad para la hora de entrada\n\nStack tecnol\u00f3gico:\nAPIs (consumo e integraci\u00f3n): REST (GET/POST/PUT/DELETE); Manejo de JSON/XML; Webhooks\nSQL: joins, filtros, agregaciones, subqueries; limpieza y preparaci\u00f3n de datasets\nHerramientas de automatizaci\u00f3n como n8n.\nFunciones: \nCrear automatizaciones para simplificar tareas repetitivas en distintos equipos (operaciones, ventas, finanzas, soporte, etc).\nConectar plataformas y herramientas para que hablen entre s\u00ed.\nExplorar casos de uso de IA (como asistentes internos, res\u00famenes autom\u00e1ticos, clasificaci\u00f3n de informaci\u00f3n, an\u00e1lisis de datos).\nDise\u00f1ar flujos de trabajo autom\u00e1ticos usando herramientas como n8n.\nColaborar con diferentes \u00e1reas para entender necesidades y transformarlas en soluciones pr\u00e1cticas.\nProbar, mejorar y mantener automatizaciones para que sean confiables y escalables.\nDocumentar ideas, procesos y mejoras (sin burocracia, pero con orden).\nCoordinarse con los diferentes proveedores IT de la compa\u00f1\u00eda\n"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "550c5a7d-42f6-4e66-94f0-e10674303d01",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1008,
        -240
      ],
      "parameters": {
        "color": 7,
        "width": 1392,
        "height": 384,
        "content": "\n\n\n\n\n\n\n\n# PART 1"
      },
      "typeVersion": 1
    },
    {
      "id": "c6f62d04-baf6-4e77-96d2-ff366d71be80",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1008,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 1392,
        "height": 384,
        "content": "\n\n\n\n\n\n\n\n# PART 2"
      },
      "typeVersion": 1
    },
    {
      "id": "49d66484-963e-4d49-a169-2c8be30640a5",
      "name": "Candidacy",
      "type": "n8n-nodes-base.gmail",
      "onError": "continueRegularOutput",
      "position": [
        -400,
        -48
      ],
      "parameters": {
        "sendTo": "={{ $('Application Reception').item.json['Correo electr\u00f3nico'] }}",
        "message": "=<div style=\"margin:0; padding:24px; background-color:#f4f6f8; font-family:Arial, Helvetica, sans-serif; color:#242424;\">\n  <div style=\"max-width:620px; margin:0 auto; background-color:#ffffff; border:1px solid #e5e7eb; border-radius:10px; overflow:hidden;\">\n\n    <div style=\"padding:24px 30px; background-color:#1f2937;\">\n      <h1 style=\"margin:0; color:#ffffff; font-size:22px; font-weight:600;\">\n        Candidatura recibida\n      </h1>\n    </div>\n\n    <div style=\"padding:30px;\">\n      <p style=\"margin:0 0 18px; font-size:16px; line-height:1.6;\">\n        Hola <strong>{{ $('Application Reception').item.json['Nombre'] }}</strong>,\n      </p>\n\n      <p style=\"margin:0 0 18px; font-size:15px; line-height:1.7;\">\n        Hemos recibido correctamente tu candidatura para el puesto de\n        <strong>T\u00e9cnico/a en Automatizaci\u00f3n de Procesos</strong>.\n      </p>\n\n      <p style=\"margin:0 0 18px; font-size:15px; line-height:1.7;\">\n        Nuestro equipo revisar\u00e1 la informaci\u00f3n proporcionada y tu curr\u00edculum.\n        La evaluaci\u00f3n automatizada se utilizar\u00e1 \u00fanicamente como apoyo, y cualquier\n        decisi\u00f3n relacionada con el proceso ser\u00e1 revisada por una persona.\n      </p>\n\n      <div style=\"margin:24px 0; padding:16px 18px; background-color:#f3f4f6; border-left:4px solid #2563eb; border-radius:4px;\">\n        <p style=\"margin:0; font-size:14px; line-height:1.6;\">\n          <strong>Estado de la candidatura:</strong> Pendiente de revisi\u00f3n\n        </p>\n      </div>\n\n      <p style=\"margin:0 0 18px; font-size:15px; line-height:1.7;\">\n        Nos pondremos en contacto contigo mediante el correo electr\u00f3nico facilitado\n        si necesitamos informaci\u00f3n adicional o si tu candidatura avanza a la siguiente fase.\n      </p>\n\n      <p style=\"margin:28px 0 0; font-size:15px; line-height:1.7;\">\n        Gracias por tu inter\u00e9s y por el tiempo dedicado a presentar tu candidatura.\n      </p>\n\n      <p style=\"margin:24px 0 0; font-size:15px; line-height:1.6;\">\n        Un saludo,<br>\n        <strong>Equipo de selecci\u00f3n de Empresa X</strong>\n      </p>\n    </div>\n\n    <div style=\"padding:18px 30px; background-color:#f9fafb; border-top:1px solid #e5e7eb;\">\n      <p style=\"margin:0; color:#6b7280; font-size:12px; line-height:1.5;\">\n        Este correo confirma \u00fanicamente la recepci\u00f3n de la candidatura y no implica\n        la aceptaci\u00f3n autom\u00e1tica en el proceso de selecci\u00f3n.\n      </p>\n    </div>\n\n  </div>\n</div>",
        "options": {},
        "subject": "Hemos recibido tu candidatura \u2013 T\u00e9cnico/a en Automatizaci\u00f3n de Procesos"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.2
    },
    {
      "id": "346ee5c0-3f2c-4cbb-9a7c-9889c8b7c731",
      "name": "Application Reception",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -832,
        -48
      ],
      "parameters": {
        "options": {
          "buttonLabel": "Enviar candidatura",
          "respondWithOptions": {
            "values": {
              "formSubmittedText": "Tu candidatura se ha enviado correctamente. Gracias por participar en el proceso de selecci\u00f3n."
            }
          }
        },
        "formTitle": "Candidatura \u2014 T\u00e9cnico/a en Automatizaci\u00f3n de Procesos",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Nombre",
              "placeholder": "Introduce tu nombre",
              "requiredField": true
            },
            {
              "fieldLabel": "Apellidos",
              "placeholder": "Introduce tus apellidos",
              "requiredField": true
            },
            {
              "fieldType": "email",
              "fieldLabel": "Correo electr\u00f3nico",
              "placeholder": "user@example.com",
              "requiredField": true
            },
            {
              "fieldLabel": "Tel\u00e9fono",
              "placeholder": "+1234567890",
              "requiredField": true
            },
            {
              "fieldLabel": "Localidad",
              "requiredField": true
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "\u00bfDispones de m\u00e1s de 2 a\u00f1os de experiencia en un puesto similar?",
              "fieldOptions": {
                "values": [
                  {
                    "option": "S\u00ed"
                  },
                  {
                    "option": "No"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldType": "number",
              "fieldLabel": "A\u00f1os de experiencia en automatizaci\u00f3n",
              "requiredField": true
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "\u00bfDispones de un nivel B1 o B2 de ingl\u00e9s?",
              "fieldOptions": {
                "values": [
                  {
                    "option": "No dispongo de nivel acreditado"
                  },
                  {
                    "option": "A1"
                  },
                  {
                    "option": "A2"
                  },
                  {
                    "option": "B1"
                  },
                  {
                    "option": "B2"
                  },
                  {
                    "option": "C1"
                  },
                  {
                    "option": "C2"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldType": "textarea",
              "fieldLabel": "Herramientas de automatizaci\u00f3n que conoces",
              "placeholder": "Por ejemplo: Power Automate, n8n, Power Apps, Python, Zapier...",
              "requiredField": true
            },
            {
              "fieldType": "textarea",
              "fieldLabel": "Describe brevemente una automatizaci\u00f3n que hayas desarrollado",
              "requiredField": true
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Disponibilidad para incorporarte",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Inmediata"
                  },
                  {
                    "option": "En 15 d\u00edas"
                  },
                  {
                    "option": "En 1 mes"
                  },
                  {
                    "option": "M\u00e1s de 1 mes"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldType": "file",
              "fieldLabel": "Curr\u00edculum",
              "requiredField": true
            },
            {
              "fieldType": "checkbox",
              "fieldLabel": "Acepto el tratamiento de mis datos para gestionar este proceso de selecci\u00f3n",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Acepto"
                  }
                ]
              }
            }
          ]
        },
        "formDescription": "Env\u00eda tu candidatura para el puesto de T\u00e9cnico/a en Automatizaci\u00f3n de Procesos en Empresa X. Los datos proporcionados se utilizar\u00e1n \u00fanicamente para gestionar el proceso de selecci\u00f3n."
      },
      "typeVersion": 2.6
    },
    {
      "id": "a53021af-de39-421f-b153-9529c943c1cb",
      "name": "Analyze CV with Ollama",
      "type": "@n8n/n8n-nodes-langchain.ollama",
      "onError": "continueErrorOutput",
      "position": [
        32,
        -48
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "nemotron-3-ultra:cloud",
          "cachedResultName": "nemotron-3-ultra:cloud"
        },
        "options": {
          "temperature": 0.2
        },
        "messages": {
          "values": [
            {
              "role": "assistant",
              "content": "=Eres un asistente especializado en selecci\u00f3n t\u00e9cnica de personal para puestos de automatizaci\u00f3n de procesos.\n\nTu funci\u00f3n es analizar curr\u00edculums y comparar exclusivamente la experiencia, formaci\u00f3n y competencias profesionales del candidato con los requisitos del puesto.\n\n{{ $('OPTIONS').item.json['business info'] }}\n\nREGLAS OBLIGATORIAS:\n1. No eval\u00faes al candidato por su nombre, localidad, edad, fotograf\u00eda, g\u00e9nero, nacionalidad, correo, tel\u00e9fono ni ninguna caracter\u00edstica personal.\n2. No inventes habilidades, a\u00f1os de experiencia, titulaciones ni conocimientos que no aparezcan expl\u00edcitamente en el curr\u00edculum.\n3. Distingue claramente entre informaci\u00f3n confirmada e informaci\u00f3n no especificada.\n4. La puntuaci\u00f3n es orientativa y nunca debe utilizarse como decisi\u00f3n autom\u00e1tica de contrataci\u00f3n o descarte.\n5. Una habilidad no mencionada debe marcarse como \"No especificado\", no como una carencia confirmada.\n6. Devuelve exclusivamente un objeto JSON v\u00e1lido.\n7. No utilices bloques Markdown, explicaciones ni texto antes o despu\u00e9s del JSON.\n8. Utiliza exactamente los nombres de campos indicados.\n\nESTRUCTURA OBLIGATORIA DE SALIDA:\n{\n  \"resumen_profesional\": \"string\",\n  \"puntuacion_total\": 0,\n  \"puntuacion_tecnica\": 0,\n  \"puntuacion_experiencia\": 0,\n  \"puntuacion_formacion\": 0,\n  \"puntuacion_proyectos\": 0,\n  \"puntuacion_complementaria\": 0,\n  \"nivel_ajuste\": \"Alto | Medio | Bajo\",\n  \"ex

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

This workflow collects job applications through an n8n form, extracts text from the uploaded PDF CV, analyzes it with Ollama, and records the results in Google Sheets while using Gmail to send candidate confirmations, route high-fit candidates to human approval, and email alerts…

Source: https://n8n.io/workflows/17433/ — 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

Automatically analyze your full sports performance evolution using your Strava activities, enriched with AI insights and delivered directly to your email — all powered by your own n8n instance.

Google Sheets, OpenAI, Stop And Error +3
AI & RAG

An n8n-based automation that generates client proposals from a form, lets you review everything in one place, and sends the proposal only when you approve it.

Form Trigger, Google Sheets Trigger, OpenAI +4
AI & RAG

The competitive edge, delivered. This Customer Intelligence Engine simultaneously analyzes the web, Reddit, and X/Twitter to generate a professional, actionable executive briefing.

Reddit, N8N Nodes Serpapi, HTTP Request +4
AI & RAG

This workflow captures website visitor form submissions, researches the visitor’s company using SerpApi, extracts structured insights and drafts a personalized outreach email with OpenAI, logs the enr

Error Trigger, Slack, Form Trigger +4
AI & RAG

Reimbursements used to be a headache.

Form Trigger, Google Drive, Gmail +2