{
  "updatedAt": "2026-06-03T10:11:59.136Z",
  "createdAt": "2026-05-25T13:51:11.721Z",
  "id": "olgL8wb7nrJumkct",
  "name": "Jobby - LinkedIn 2 Notion",
  "description": null,
  "active": true,
  "isArchived": false,
  "nodes": [
    {
      "parameters": {
        "promptType": "define",
        "text": "=Company: {{ $('CV Factory').item.json.query.company }}\njon title : {{ $('CV Factory').item.json.query.job_title }}\njob Url: {{ $('CV Factory').item.json.query.job_url }}\nDescription: {{ $('CV Factory').item.json.query.job_description }}\n",
        "options": {
          "systemMessage": "=# Tu es Jobby 3.0\nL'assistant strat\u00e9gique de recherche d'imploi pour Julien. \n\n## Identity & Tone\nTon ton est neutre, analytique et orient\u00e9 r\u00e9sultats, comme un coach en recrutement senior. Ta valeur ajout\u00e9e r\u00e9side dans la d\u00e9tection pr\u00e9cise des \u00e9carts techniques et organisationnels.\n\n## Purpose\n\u00c9valuer l'ad\u00e9quation entre le profil de Julien (Solutions Engineer, expert automation/IA) et une offre d'emploi sp\u00e9cifique, puis g\u00e9n\u00e9rer le CV Markdown optimal pour maximiser le score ATS.\n\n## Context & Data\nPour r\u00e9aliser cette analyse et cette g\u00e9n\u00e9ration, voici les donn\u00e9es consolid\u00e9es issues du syst\u00e8me :\n\n## 1. Fiche de Poste Aspir\u00e9e (LinkedIn)\n{{ $json.job_description }}\n\n## 2. Directives de Positionnement (La Graine)\n- Baseline attendue : {{ $json.seed_baseline }}\n- Consignes de filtrage : {{ $json.seed_directives }}\n\n## 3. Graphe de Comp\u00e9tences & Exp\u00e9riences (CV Atomique)\n{{ $json.cv_master_payload }}\n\n# Evaluation Criteria\n1. Score (1-10) : Bas\u00e9 strictement sur l'ad\u00e9quation technique, l'exp\u00e9rience pass\u00e9e et le niveau de s\u00e9niorit\u00e9 requis.\n2. Gap Analysis : Identifier les technologies ou m\u00e9thodologies manquantes sans fioritures.\n3. Reskilling & Tailoring : S\u00e9lectionner uniquement les 4 derni\u00e8res activit\u00e9s du CV Atomique et adapter subtilement les puces d'impact en utilisant les mots-cl\u00e9s de la fiche de poste et l'orientation de la Graine.\n\n**CRITICAL NOTION LIMIT**: Le texte dans `\"generated_cv_markdown\"` doit \u00eatre concis et synth\u00e9tique. Chaque section `(Summary, Exp\u00e9rience, etc.)``  doit \u00eatre s\u00e9par\u00e9e par le caract\u00e8re \"|\" pour nous permettre de d\u00e9couper le texte apr\u00e8s coup et \u00e9viter de d\u00e9passer la limite de 2000 caract\u00e8res par bloc impos\u00e9e par l'API Notion.\n\n# Output Format (STRICT JSON)\nR\u00e9ponds exclusivement par un objet JSON valide. Le CV au format Markdown et en anglais doit \u00eatre enti\u00e8rement encapsul\u00e9 dans la cl\u00e9 \"generated_cv_markdown\". Ne mets pas de balises de code (comme `markdown`) \u00e0 l'int\u00e9rieur du JSON.\n\n```{\n  \"domain\": \"string (ex: Generative AI, Automation, API Management)\",\n  \"job_title\": \"string (Titre exact)\",\n  \"company\": \"string\",\n  \"match_score\": number,\n  \"quick_summary\": \"string (MAX 150 caract\u00e8res)\",\n  \"analysis_pros\": \"string (Points forts techniques et m\u00e9tier - Max 800 caract\u00e8res)\",\n  \"analysis_cons\": \"string (Points de friction et manques - Max 800 caract\u00e8res)\",\n  \"action_plan\": \"string (Conseil imm\u00e9diat pour la candidature - Max 400 caract\u00e8res)\",\n  \"generated_cv_markdown\": \"string (Le CV V2.0 complet en Markdown, structur\u00e9 avec l'Aside de d\u00e9part contenant la baseline, puis les 4 derniers jobs adapt\u00e9s aux crit\u00e8res ATS)\"\n}"
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [
        352,
        48
      ],
      "id": "b28e3fb0-c3bf-42f4-9a63-a95c5c8d4055",
      "name": "AI Agent",
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "modelName": "models/gemini-2.5-flash-lite",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "typeVersion": 1.1,
      "position": [
        352,
        320
      ],
      "id": "c09c542b-80ca-4d47-a542-15fc14eabccd",
      "name": "Google Gemini Chat Model",
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "claude-sonnet-4-6",
          "cachedResultName": "Claude Sonnet 4.6"
        },
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.4,
      "position": [
        -544,
        384
      ],
      "id": "29dd6f16-98a6-48f5-ad0b-7a76b5eb335a",
      "name": "Anthropic Chat Model",
      "credentials": {
        "anthropicApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// 1. On r\u00e9cup\u00e8re le texte g\u00e9n\u00e9r\u00e9 par l'Agent\nconst text = $json.output || \"\"; \nconst match = text.match(/\\{[\\s\\S]*\\}/);\n\nif (match) {\n  try {\n    // On parse uniquement le bloc JSON trouv\u00e9\n    const data = JSON.parse(match);\n\n    // Helper pour les tableaux Notion (Multi-select)\n    const toSimpleArray = (val) => {\n      if (!val) return [];\n      const rawItems = Array.isArray(val) ? val : val.toString().split(/[|,\\/&]|\\band\\b/);\n      return rawItems.map(i => i.trim()).filter(i => i.length > 1);\n    };\n\n    // 2. On reconstruit l'objet propre pour alimenter le n\u0153ud \"Update Notion\"\n    return [{\n      json: {\n        domain: toSimpleArray(data.domain),\n        category: toSimpleArray(data.category || data.job_title),\n        job_title: data.job_title || \"Position inconnue\",\n        company: data.company || \"Entreprise inconnue\",\n        score: parseInt(data.match_score) || 0,\n        status: \"To review\",\n        summary: data.quick_summary || \"Analyse termin\u00e9e\",\n        analysis_pros: data.analysis_pros || \"N/A\",\n        analysis_cons: data.analysis_cons || \"N/A\",\n        action_plan: data.action_plan || \"N/A\",\n        generated_cv_markdown: data.generated_cv_markdown || \"\",\n        response_date: new Date().toISOString().split('T')\n      }\n    }];\n\n  } catch (e) {\n    return [{ json: { error: \"JSON Parsing Error\", message: e.message, raw: text.substring(0, 200) } }];\n  }\n}\n\nreturn [{ json: { error: \"No JSON found in AI output\", raw: text.substring(0, 100) } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        704,
        -48
      ],
      "id": "bc6d9e53-7c47-4b19-97f4-e78868ea7cdd",
      "name": "JSON Cleaner",
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "resource": "database",
        "databaseId": {
          "__rl": true,
          "value": "5ea1407d-50b4-4527-bd86-21f3c58535fa",
          "mode": "list",
          "cachedResultName": "Complete Resume DB",
          "cachedResultUrl": "https://www.notion.so/5ea1407d50b44527bd8621f3c58535fa"
        }
      },
      "type": "n8n-nodes-base.notion",
      "typeVersion": 2.2,
      "position": [
        -320,
        48
      ],
      "id": "98b735c7-3087-4ff3-83a7-c749cba35b8b",
      "name": "Get CV data",
      "credentials": {
        "notionApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "resource": "database",
        "databaseId": {
          "__rl": true,
          "value": "642d04c9-1c1f-4c01-a4d3-89822f02556b",
          "mode": "list",
          "cachedResultName": "CV Seeds",
          "cachedResultUrl": "https://www.notion.so/642d04c91c1f4c01a4d389822f02556b"
        }
      },
      "type": "n8n-nodes-base.notion",
      "typeVersion": 2.2,
      "position": [
        -96,
        48
      ],
      "id": "1c3e6440-cbf2-472c-ab63-959a496a7e6f",
      "name": "CV Seed",
      "credentials": {
        "notionApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "resource": "databasePage",
        "databaseId": {
          "__rl": true,
          "value": "127bad1f-b25a-4b6b-8eec-7b342e3aa504",
          "mode": "list",
          "cachedResultName": "Linked Detector",
          "cachedResultUrl": "https://www.notion.so/127bad1fb25a4b6b8eec7b342e3aa504"
        },
        "title": "LinkedIn Detector",
        "simple": false,
        "propertiesUi": {
          "propertyValues": [
            {
              "key": "Job Title|title",
              "title": "={{ $json.job_title || \"Nouvelle offre LinkedIn (Titre non extrait)\" }}\n"
            },
            {
              "key": "category|multi_select",
              "multiSelectValue": "={{ $json.category }}"
            },
            {
              "key": "URL|url",
              "urlValue": "={{ $('CV Factory').item.json.query.job_url }}"
            },
            {
              "key": "Match Score|number",
              "numberValue": "={{ $json.score }}"
            },
            {
              "key": "Status|status",
              "statusValue": "={{ $json.status }}"
            },
            {
              "key": "Company|select",
              "selectValue": "={{ $json.company }}"
            },
            {
              "key": "domain|multi_select",
              "multiSelectValue": "={{ $json.domain }}S"
            },
            {
              "key": "analysis|rich_text",
              "textContent": "={{ $json.summary }}"
            }
          ]
        },
        "blockUi": {
          "blockValues": [
            {
              "type": "heading_1",
              "textContent": "Analyse compl\u00e8te"
            },
            {
              "textContent": "={{ $json.summary }}"
            },
            {
              "type": "heading_2",
              "textContent": "Pros"
            },
            {
              "textContent": "={{ $json.analysis_pros }}"
            },
            {
              "type": "heading_2",
              "textContent": "Cons"
            },
            {
              "textContent": "={{ $json.analysis_cons }}"
            },
            {
              "type": "heading_2",
              "textContent": "Action Plan"
            },
            {
              "textContent": "={{ $json.action_plan }}"
            },
            {
              "type": "heading_1",
              "textContent": "Raw job description "
            },
            {
              "type": "toggle",
              "textContent": "=About the job"
            },
            {
              "textContent": "={{ $('CV Factory').item.json.query.job_description }}"
            }
          ]
        },
        "options": {
          "iconType": "emoji",
          "icon": "\ud83c\udd95"
        }
      },
      "type": "n8n-nodes-base.notion",
      "typeVersion": 2.2,
      "position": [
        928,
        -48
      ],
      "id": "a801be3c-190f-4c32-a564-050e9ba194f2",
      "name": "update prospect",
      "retryOnFail": false,
      "executeOnce": false,
      "credentials": {
        "notionApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "path": "cv-factory",
        "authentication": "headerAuth",
        "options": {
          "responseData": "{\"status\": \"processing\"}"
        }
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -544,
        48
      ],
      "id": "4f70d8ad-1003-4506-b630-04d85c94d2e1",
      "name": "CV Factory",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// 1. R\u00e9cup\u00e9ration des lignes du CV Atomique\nlet cvRows = [];\ntry {\n  cvRows = $('Get CV data').all();\n} catch (e) {\n  cvRows = [];\n}\n\n// Extraction universelle pour le CV\nconst cleanExperiences = cvRows.map((row, index) => {\n  const props = row.json?.properties || {};\n  \n  // Fonction helper pour extraire le texte brut d'une propri\u00e9t\u00e9 Notion, quel que soit son type (title ou rich_text)\n  const extractText = (propObj) => {\n    if (!propObj) return \"\";\n    const arr = propObj.rich_text || propObj.title || [];\n    return arr?.plain_text || propObj.value || \"\";\n  };\n\n  // On scanne dynamiquement les variantes de noms de colonnes possibles\n  const entreprise = extractText(props.Entreprise) || extractText(props.Company) || \"Entreprise \" + (index + 1);\n  const poste = extractText(props.Poste) || extractText(props.Role) || \"Solutions Engineer\";\n  const description = extractText(props.Description) || \"\";\n  const dates = extractText(props.Dates) || extractText(props.Date) || \"\";\n\n  return `### ${poste} chez ${entreprise} (${dates})\\n${description}`;\n}).join(\"\\n\\n\");\n\n// 2. R\u00e9cup\u00e9ration et isolation de la Graine UNIQUE\nlet seedBaseline = \"Solutions Engineer\";\nlet seedDirectives = \"Standard tailoring\";\n\ntry {\n  const allSeeds = $('CV Seed').all();\n  // On cherche la graine active via la checkbox, sinon on prend la premi\u00e8re disponible\n  const selectedSeedItem = allSeeds.find(item => {\n    const props = item.json?.properties || {};\n    // G\u00e8re la checkbox Notion (qu'elle s'appelle Active ou active)\n    const checkObj = props.Active || props.active;\n    return checkObj?.checkbox === true;\n  }) || allSeeds;\n\n  if (selectedSeedItem && selectedSeedItem.json?.properties) {\n    const sProps = selectedSeedItem.json.properties;\n    \n    const extractSeedText = (propObj) => {\n      if (!propObj) return null;\n      const arr = propObj.title || propObj.rich_text || [];\n      return arr?.plain_text || null;\n    };\n\n    seedBaseline = extractSeedText(sProps.Baseline) || extractSeedText(sProps.baseline) || seedBaseline;\n    seedDirectives = extractSeedText(sProps.Directives) || extractSeedText(sProps.directives) || seedDirectives;\n  }\n} catch (e) {\n  // En cas de probl\u00e8me de lecture de la graine, on garde les valeurs par d\u00e9faut pour ne pas bloquer le flux\n}\n\n// 3. Payload plat final garanti sans crash\nreturn [{\n  json: {\n    job_description: $json.job_description || \"Pas de description\",\n    seed_baseline: seedBaseline,\n    seed_directives: seedDirectives,\n    cv_master_payload: cleanExperiences || \"CV Master vide\"\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        128,
        48
      ],
      "id": "6fc7730a-c6d3-48db-919d-c700c1c6bed1",
      "name": "Clean CV"
    },
    {
      "parameters": {
        "errorMessage": "={{ $('AI Agent').item.json.error }}"
      },
      "type": "n8n-nodes-base.stopAndError",
      "typeVersion": 1,
      "position": [
        704,
        144
      ],
      "id": "0334fb31-bc67-410f-89bc-7e0de9ccaa43",
      "name": "Stop and Error"
    }
  ],
  "connections": {
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "JSON Cleaner",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Stop and Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anthropic Chat Model": {
      "ai_languageModel": [
        []
      ]
    },
    "JSON Cleaner": {
      "main": [
        [
          {
            "node": "update prospect",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Get CV data": {
      "main": [
        [
          {
            "node": "CV Seed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CV Seed": {
      "main": [
        [
          {
            "node": "Clean CV",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "update prospect": {
      "main": [
        [],
        []
      ]
    },
    "CV Factory": {
      "main": [
        [
          {
            "node": "Get CV data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clean CV": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "timeSavedMode": "fixed",
    "timezone": "Europe/Paris",
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": false
  },
  "staticData": null,
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "versionId": "1f38b6c8-cab7-4bb2-a645-f6c092f23ca5",
  "activeVersionId": "1f38b6c8-cab7-4bb2-a645-f6c092f23ca5",
  "versionCounter": 167,
  "triggerCount": 1,
  "shared": [
    {
      "updatedAt": "2026-05-25T13:51:11.748Z",
      "createdAt": "2026-05-25T13:51:11.748Z",
      "role": "workflow:owner",
      "workflowId": "olgL8wb7nrJumkct",
      "projectId": "GfFdmZTqGEJQkrXG",
      "project": {
        "updatedAt": "2026-05-11T09:44:44.757Z",
        "createdAt": "2026-05-11T09:34:45.695Z",
        "id": "GfFdmZTqGEJQkrXG",
        "name": "\u00c9ole Wind <megazef@gmail.com>",
        "type": "personal",
        "icon": null,
        "description": null,
        "creatorId": "2792484d-cba3-4156-adba-fbc49134eb55"
      }
    }
  ],
  "tags": [
    {
      "updatedAt": "2026-05-29T11:31:39.972Z",
      "createdAt": "2026-05-29T11:31:39.972Z",
      "id": "5zynHpPHQi9PRMyD",
      "name": "jobby"
    },
    {
      "updatedAt": "2026-05-11T12:19:25.326Z",
      "createdAt": "2026-05-11T12:19:25.326Z",
      "id": "8yiCAJZW8fFXfFqI",
      "name": "notion"
    },
    {
      "updatedAt": "2026-05-29T11:33:37.867Z",
      "createdAt": "2026-05-29T11:33:37.867Z",
      "id": "oVl5DgcCQROKFX7w",
      "name": "AI"
    },
    {
      "updatedAt": "2026-05-29T11:33:25.261Z",
      "createdAt": "2026-05-29T11:33:25.261Z",
      "id": "sTPsdvgfTYjdu5g7",
      "name": "live"
    }
  ],
  "activeVersion": {
    "updatedAt": "2026-06-03T10:12:33.000Z",
    "createdAt": "2026-06-03T10:11:59.138Z",
    "versionId": "1f38b6c8-cab7-4bb2-a645-f6c092f23ca5",
    "workflowId": "olgL8wb7nrJumkct",
    "nodes": [
      {
        "parameters": {
          "promptType": "define",
          "text": "=Company: {{ $('CV Factory').item.json.query.company }}\njon title : {{ $('CV Factory').item.json.query.job_title }}\njob Url: {{ $('CV Factory').item.json.query.job_url }}\nDescription: {{ $('CV Factory').item.json.query.job_description }}\n",
          "options": {
            "systemMessage": "=# Tu es Jobby 3.0\nL'assistant strat\u00e9gique de recherche d'imploi pour Julien. \n\n## Identity & Tone\nTon ton est neutre, analytique et orient\u00e9 r\u00e9sultats, comme un coach en recrutement senior. Ta valeur ajout\u00e9e r\u00e9side dans la d\u00e9tection pr\u00e9cise des \u00e9carts techniques et organisationnels.\n\n## Purpose\n\u00c9valuer l'ad\u00e9quation entre le profil de Julien (Solutions Engineer, expert automation/IA) et une offre d'emploi sp\u00e9cifique, puis g\u00e9n\u00e9rer le CV Markdown optimal pour maximiser le score ATS.\n\n## Context & Data\nPour r\u00e9aliser cette analyse et cette g\u00e9n\u00e9ration, voici les donn\u00e9es consolid\u00e9es issues du syst\u00e8me :\n\n## 1. Fiche de Poste Aspir\u00e9e (LinkedIn)\n{{ $json.job_description }}\n\n## 2. Directives de Positionnement (La Graine)\n- Baseline attendue : {{ $json.seed_baseline }}\n- Consignes de filtrage : {{ $json.seed_directives }}\n\n## 3. Graphe de Comp\u00e9tences & Exp\u00e9riences (CV Atomique)\n{{ $json.cv_master_payload }}\n\n# Evaluation Criteria\n1. Score (1-10) : Bas\u00e9 strictement sur l'ad\u00e9quation technique, l'exp\u00e9rience pass\u00e9e et le niveau de s\u00e9niorit\u00e9 requis.\n2. Gap Analysis : Identifier les technologies ou m\u00e9thodologies manquantes sans fioritures.\n3. Reskilling & Tailoring : S\u00e9lectionner uniquement les 4 derni\u00e8res activit\u00e9s du CV Atomique et adapter subtilement les puces d'impact en utilisant les mots-cl\u00e9s de la fiche de poste et l'orientation de la Graine.\n\n**CRITICAL NOTION LIMIT**: Le texte dans `\"generated_cv_markdown\"` doit \u00eatre concis et synth\u00e9tique. Chaque section `(Summary, Exp\u00e9rience, etc.)``  doit \u00eatre s\u00e9par\u00e9e par le caract\u00e8re \"|\" pour nous permettre de d\u00e9couper le texte apr\u00e8s coup et \u00e9viter de d\u00e9passer la limite de 2000 caract\u00e8res par bloc impos\u00e9e par l'API Notion.\n\n# Output Format (STRICT JSON)\nR\u00e9ponds exclusivement par un objet JSON valide. Le CV au format Markdown et en anglais doit \u00eatre enti\u00e8rement encapsul\u00e9 dans la cl\u00e9 \"generated_cv_markdown\". Ne mets pas de balises de code (comme `markdown`) \u00e0 l'int\u00e9rieur du JSON.\n\n```{\n  \"domain\": \"string (ex: Generative AI, Automation, API Management)\",\n  \"job_title\": \"string (Titre exact)\",\n  \"company\": \"string\",\n  \"match_score\": number,\n  \"quick_summary\": \"string (MAX 150 caract\u00e8res)\",\n  \"analysis_pros\": \"string (Points forts techniques et m\u00e9tier - Max 800 caract\u00e8res)\",\n  \"analysis_cons\": \"string (Points de friction et manques - Max 800 caract\u00e8res)\",\n  \"action_plan\": \"string (Conseil imm\u00e9diat pour la candidature - Max 400 caract\u00e8res)\",\n  \"generated_cv_markdown\": \"string (Le CV V2.0 complet en Markdown, structur\u00e9 avec l'Aside de d\u00e9part contenant la baseline, puis les 4 derniers jobs adapt\u00e9s aux crit\u00e8res ATS)\"\n}"
          }
        },
        "type": "@n8n/n8n-nodes-langchain.agent",
        "typeVersion": 3.1,
        "position": [
          352,
          48
        ],
        "id": "b28e3fb0-c3bf-42f4-9a63-a95c5c8d4055",
        "name": "AI Agent",
        "onError": "continueErrorOutput"
      },
      {
        "parameters": {
          "modelName": "models/gemini-2.5-flash-lite",
          "options": {}
        },
        "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
        "typeVersion": 1.1,
        "position": [
          352,
          320
        ],
        "id": "c09c542b-80ca-4d47-a542-15fc14eabccd",
        "name": "Google Gemini Chat Model",
        "credentials": {
          "googlePalmApi": {
            "id": "QZGttnIQoPa4rsAg",
            "name": "Google Gemini(PaLM) Api account"
          }
        }
      },
      {
        "parameters": {
          "model": {
            "__rl": true,
            "mode": "list",
            "value": "claude-sonnet-4-6",
            "cachedResultName": "Claude Sonnet 4.6"
          },
          "options": {}
        },
        "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
        "typeVersion": 1.4,
        "position": [
          -544,
          384
        ],
        "id": "29dd6f16-98a6-48f5-ad0b-7a76b5eb335a",
        "name": "Anthropic Chat Model",
        "credentials": {
          "anthropicApi": {
            "id": "ZLxe6Yv6rEgDYs00",
            "name": "Anthropic account"
          }
        }
      },
      {
        "parameters": {
          "jsCode": "// 1. On r\u00e9cup\u00e8re le texte g\u00e9n\u00e9r\u00e9 par l'Agent\nconst text = $json.output || \"\"; \nconst match = text.match(/\\{[\\s\\S]*\\}/);\n\nif (match) {\n  try {\n    // On parse uniquement le bloc JSON trouv\u00e9\n    const data = JSON.parse(match);\n\n    // Helper pour les tableaux Notion (Multi-select)\n    const toSimpleArray = (val) => {\n      if (!val) return [];\n      const rawItems = Array.isArray(val) ? val : val.toString().split(/[|,\\/&]|\\band\\b/);\n      return rawItems.map(i => i.trim()).filter(i => i.length > 1);\n    };\n\n    // 2. On reconstruit l'objet propre pour alimenter le n\u0153ud \"Update Notion\"\n    return [{\n      json: {\n        domain: toSimpleArray(data.domain),\n        category: toSimpleArray(data.category || data.job_title),\n        job_title: data.job_title || \"Position inconnue\",\n        company: data.company || \"Entreprise inconnue\",\n        score: parseInt(data.match_score) || 0,\n        status: \"To review\",\n        summary: data.quick_summary || \"Analyse termin\u00e9e\",\n        analysis_pros: data.analysis_pros || \"N/A\",\n        analysis_cons: data.analysis_cons || \"N/A\",\n        action_plan: data.action_plan || \"N/A\",\n        generated_cv_markdown: data.generated_cv_markdown || \"\",\n        response_date: new Date().toISOString().split('T')\n      }\n    }];\n\n  } catch (e) {\n    return [{ json: { error: \"JSON Parsing Error\", message: e.message, raw: text.substring(0, 200) } }];\n  }\n}\n\nreturn [{ json: { error: \"No JSON found in AI output\", raw: text.substring(0, 100) } }];"
        },
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          704,
          -48
        ],
        "id": "bc6d9e53-7c47-4b19-97f4-e78868ea7cdd",
        "name": "JSON Cleaner",
        "onError": "continueErrorOutput"
      },
      {
        "parameters": {
          "resource": "database",
          "databaseId": {
            "__rl": true,
            "value": "5ea1407d-50b4-4527-bd86-21f3c58535fa",
            "mode": "list",
            "cachedResultName": "Complete Resume DB",
            "cachedResultUrl": "https://www.notion.so/5ea1407d50b44527bd8621f3c58535fa"
          }
        },
        "type": "n8n-nodes-base.notion",
        "typeVersion": 2.2,
        "position": [
          -320,
          48
        ],
        "id": "98b735c7-3087-4ff3-83a7-c749cba35b8b",
        "name": "Get CV data",
        "credentials": {
          "notionApi": {
            "id": "KYrsxF0plfLkPpqW",
            "name": "Notion LinkedIn Auto"
          }
        }
      },
      {
        "parameters": {
          "resource": "database",
          "databaseId": {
            "__rl": true,
            "value": "642d04c9-1c1f-4c01-a4d3-89822f02556b",
            "mode": "list",
            "cachedResultName": "CV Seeds",
            "cachedResultUrl": "https://www.notion.so/642d04c91c1f4c01a4d389822f02556b"
          }
        },
        "type": "n8n-nodes-base.notion",
        "typeVersion": 2.2,
        "position": [
          -96,
          48
        ],
        "id": "1c3e6440-cbf2-472c-ab63-959a496a7e6f",
        "name": "CV Seed",
        "credentials": {
          "notionApi": {
            "id": "KYrsxF0plfLkPpqW",
            "name": "Notion LinkedIn Auto"
          }
        }
      },
      {
        "parameters": {
          "resource": "databasePage",
          "databaseId": {
            "__rl": true,
            "value": "127bad1f-b25a-4b6b-8eec-7b342e3aa504",
            "mode": "list",
            "cachedResultName": "Linked Detector",
            "cachedResultUrl": "https://www.notion.so/127bad1fb25a4b6b8eec7b342e3aa504"
          },
          "title": "LinkedIn Detector",
          "simple": false,
          "propertiesUi": {
            "propertyValues": [
              {
                "key": "Job Title|title",
                "title": "={{ $json.job_title || \"Nouvelle offre LinkedIn (Titre non extrait)\" }}\n"
              },
              {
                "key": "category|multi_select",
                "multiSelectValue": "={{ $json.category }}"
              },
              {
                "key": "URL|url",
                "urlValue": "={{ $('CV Factory').item.json.query.job_url }}"
              },
              {
                "key": "Match Score|number",
                "numberValue": "={{ $json.score }}"
              },
              {
                "key": "Status|status",
                "statusValue": "={{ $json.status }}"
              },
              {
                "key": "Company|select",
                "selectValue": "={{ $json.company }}"
              },
              {
                "key": "domain|multi_select",
                "multiSelectValue": "={{ $json.domain }}S"
              },
              {
                "key": "analysis|rich_text",
                "textContent": "={{ $json.summary }}"
              }
            ]
          },
          "blockUi": {
            "blockValues": [
              {
                "type": "heading_1",
                "textContent": "Analyse compl\u00e8te"
              },
              {
                "textContent": "={{ $json.summary }}"
              },
              {
                "type": "heading_2",
                "textContent": "Pros"
              },
              {
                "textContent": "={{ $json.analysis_pros }}"
              },
              {
                "type": "heading_2",
                "textContent": "Cons"
              },
              {
                "textContent": "={{ $json.analysis_cons }}"
              },
              {
                "type": "heading_2",
                "textContent": "Action Plan"
              },
              {
                "textContent": "={{ $json.action_plan }}"
              },
              {
                "type": "heading_1",
                "textContent": "Raw job description "
              },
              {
                "type": "toggle",
                "textContent": "=About the job"
              },
              {
                "textContent": "={{ $('CV Factory').item.json.query.job_description }}"
              }
            ]
          },
          "options": {
            "iconType": "emoji",
            "icon": "\ud83c\udd95"
          }
        },
        "type": "n8n-nodes-base.notion",
        "typeVersion": 2.2,
        "position": [
          928,
          -48
        ],
        "id": "a801be3c-190f-4c32-a564-050e9ba194f2",
        "name": "update prospect",
        "retryOnFail": false,
        "executeOnce": false,
        "credentials": {
          "notionApi": {
            "id": "KYrsxF0plfLkPpqW",
            "name": "Notion LinkedIn Auto"
          }
        }
      },
      {
        "parameters": {
          "path": "cv-factory",
          "authentication": "headerAuth",
          "options": {
            "responseData": "{\"status\": \"processing\"}"
          }
        },
        "type": "n8n-nodes-base.webhook",
        "typeVersion": 2.1,
        "position": [
          -544,
          48
        ],
        "id": "4f70d8ad-1003-4506-b630-04d85c94d2e1",
        "name": "CV Factory",
        "webhookId": "44786c94-5bac-493e-a778-8d065668d8b2",
        "credentials": {
          "httpHeaderAuth": {
            "id": "QDm4dLNTsiXmLpmM",
            "name": "Header Auth account"
          }
        }
      },
      {
        "parameters": {
          "jsCode": "// 1. R\u00e9cup\u00e9ration des lignes du CV Atomique\nlet cvRows = [];\ntry {\n  cvRows = $('Get CV data').all();\n} catch (e) {\n  cvRows = [];\n}\n\n// Extraction universelle pour le CV\nconst cleanExperiences = cvRows.map((row, index) => {\n  const props = row.json?.properties || {};\n  \n  // Fonction helper pour extraire le texte brut d'une propri\u00e9t\u00e9 Notion, quel que soit son type (title ou rich_text)\n  const extractText = (propObj) => {\n    if (!propObj) return \"\";\n    const arr = propObj.rich_text || propObj.title || [];\n    return arr?.plain_text || propObj.value || \"\";\n  };\n\n  // On scanne dynamiquement les variantes de noms de colonnes possibles\n  const entreprise = extractText(props.Entreprise) || extractText(props.Company) || \"Entreprise \" + (index + 1);\n  const poste = extractText(props.Poste) || extractText(props.Role) || \"Solutions Engineer\";\n  const description = extractText(props.Description) || \"\";\n  const dates = extractText(props.Dates) || extractText(props.Date) || \"\";\n\n  return `### ${poste} chez ${entreprise} (${dates})\\n${description}`;\n}).join(\"\\n\\n\");\n\n// 2. R\u00e9cup\u00e9ration et isolation de la Graine UNIQUE\nlet seedBaseline = \"Solutions Engineer\";\nlet seedDirectives = \"Standard tailoring\";\n\ntry {\n  const allSeeds = $('CV Seed').all();\n  // On cherche la graine active via la checkbox, sinon on prend la premi\u00e8re disponible\n  const selectedSeedItem = allSeeds.find(item => {\n    const props = item.json?.properties || {};\n    // G\u00e8re la checkbox Notion (qu'elle s'appelle Active ou active)\n    const checkObj = props.Active || props.active;\n    return checkObj?.checkbox === true;\n  }) || allSeeds;\n\n  if (selectedSeedItem && selectedSeedItem.json?.properties) {\n    const sProps = selectedSeedItem.json.properties;\n    \n    const extractSeedText = (propObj) => {\n      if (!propObj) return null;\n      const arr = propObj.title || propObj.rich_text || [];\n      return arr?.plain_text || null;\n    };\n\n    seedBaseline = extractSeedText(sProps.Baseline) || extractSeedText(sProps.baseline) || seedBaseline;\n    seedDirectives = extractSeedText(sProps.Directives) || extractSeedText(sProps.directives) || seedDirectives;\n  }\n} catch (e) {\n  // En cas de probl\u00e8me de lecture de la graine, on garde les valeurs par d\u00e9faut pour ne pas bloquer le flux\n}\n\n// 3. Payload plat final garanti sans crash\nreturn [{\n  json: {\n    job_description: $json.job_description || \"Pas de description\",\n    seed_baseline: seedBaseline,\n    seed_directives: seedDirectives,\n    cv_master_payload: cleanExperiences || \"CV Master vide\"\n  }\n}];"
        },
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          128,
          48
        ],
        "id": "6fc7730a-c6d3-48db-919d-c700c1c6bed1",
        "name": "Clean CV"
      },
      {
        "parameters": {
          "errorMessage": "={{ $('AI Agent').item.json.error }}"
        },
        "type": "n8n-nodes-base.stopAndError",
        "typeVersion": 1,
        "position": [
          704,
          144
        ],
        "id": "0334fb31-bc67-410f-89bc-7e0de9ccaa43",
        "name": "Stop and Error"
      }
    ],
    "connections": {
      "Google Gemini Chat Model": {
        "ai_languageModel": [
          [
            {
              "node": "AI Agent",
              "type": "ai_languageModel",
              "index": 0
            }
          ]
        ]
      },
      "AI Agent": {
        "main": [
          [
            {
              "node": "JSON Cleaner",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Stop and Error",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Anthropic Chat Model": {
        "ai_languageModel": [
          []
        ]
      },
      "JSON Cleaner": {
        "main": [
          [
            {
              "node": "update prospect",
              "type": "main",
              "index": 0
            }
          ],
          []
        ]
      },
      "Get CV data": {
        "main": [
          [
            {
              "node": "CV Seed",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "CV Seed": {
        "main": [
          [
            {
              "node": "Clean CV",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "update prospect": {
        "main": [
          [],
          []
        ]
      },
      "CV Factory": {
        "main": [
          [
            {
              "node": "Get CV data",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Clean CV": {
        "main": [
          [
            {
              "node": "AI Agent",
              "type": "main",
              "index": 0
            }
          ]
        ]
      }
    },
    "authors": "\u00c9ole Wind",
    "name": "Version 1f38b6c8",
    "description": "Authorization",
    "autosaved": true,
    "workflowPublishHistory": [
      {
        "createdAt": "2026-06-03T10:12:33.619Z",
        "id": 208,
        "workflowId": "olgL8wb7nrJumkct",
        "versionId": "1f38b6c8-cab7-4bb2-a645-f6c092f23ca5",
        "event": "activated",
        "userId": "2792484d-cba3-4156-adba-fbc49134eb55"
      }
    ]
  }
}