{
  "name": "\ud83d\udd0d Recherche Alternance Cybers\u00e9curit\u00e9 - Complet",
  "nodes": [
    {
      "parameters": {},
      "id": "start-node-001",
      "name": "\ud83d\ude80 Start",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        120,
        300
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "search-terms-id",
              "name": "recherche",
              "value": "alternance cybers\u00e9curit\u00e9",
              "type": "string"
            },
            {
              "id": "location-id",
              "name": "localisation",
              "value": "France",
              "type": "string"
            },
            {
              "id": "sites-id",
              "name": "sites",
              "value": "[\n  \"https://www.apec.fr/candidat/recherche-emploi.html/emploi?motsCles=alternance%20cybers\u00e9curit\u00e9\",\n  \"https://fr.indeed.com/jobs?q=alternance+cybers\u00e9curit\u00e9\",\n  \"https://labonnealternance.pole-emploi.fr/recherche-apprentissage?&job=cybers\u00e9curit\u00e9\",\n  \"https://www.francetravail.fr/candidat/rechercheoffres/resultats/recherche?offresPartenaires=true&range=0-19&sort=0&nature=1&q=alternance%20cybers\u00e9curit\u00e9\",\n  \"https://walt.community/jobs?search=cybers\u00e9curit\u00e9%20alternance\",\n  \"https://www.bloom-alternance.fr/recherche?q=cybers\u00e9curit\u00e9\"\n]",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "config-node-002",
      "name": "\u2699\ufe0f Configuration Recherche",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        320,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// SCRAPER MULTI-SITES POUR ALTERNANCES CYBERS\u00c9CURIT\u00c9\n\nconst config = $input.item.json;\nconst sites = JSON.parse(config.sites);\n\nconsole.log('\ud83d\udd0d === D\u00c9BUT SCRAPING MULTI-SITES ===');\nconsole.log('\ud83d\udcdd Recherche:', config.recherche);\nconsole.log('\ud83d\udccd Localisation:', config.localisation);\nconsole.log('\ud83c\udf10 Sites \u00e0 scraper:', sites.length);\n\nconst offresCollectees = [];\n\n// Fonction de scraping pour chaque site\nasync function scraperSite(url, siteName) {\n  try {\n    console.log(`\ud83c\udf10 Scraping ${siteName}: ${url}`);\n    \n    const response = await fetch(url, {\n      headers: {\n        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',\n        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\n        'Accept-Language': 'fr-FR,fr;q=0.5',\n        'Accept-Encoding': 'gzip, deflate',\n        'Connection': 'keep-alive',\n        'Upgrade-Insecure-Requests': '1'\n      }\n    });\n\n    if (!response.ok) {\n      console.log(`\u274c Erreur ${siteName}: HTTP ${response.status}`);\n      return [];\n    }\n\n    const html = await response.text();\n    console.log(`\ud83d\udcc4 HTML re\u00e7u de ${siteName}: ${html.length} caract\u00e8res`);\n    \n    // Simulation d'extraction (\u00e0 adapter selon chaque site)\n    const offres = extraireOffres(html, siteName, url);\n    console.log(`\u2705 ${siteName}: ${offres.length} offres trouv\u00e9es`);\n    \n    return offres;\n    \n  } catch (error) {\n    console.log(`\ud83d\udea8 Erreur scraping ${siteName}:`, error.message);\n    return [];\n  }\n}\n\n// Extraction des offres selon le site\nfunction extraireOffres(html, siteName, url) {\n  // Patterns de recherche pour chaque site\n  const patterns = {\n    'apec.fr': {\n      titleRegex: /<h3[^>]*class=\"[^\"]*title[^\"]*\"[^>]*>([^<]+)<\\/h3>/gi,\n      companyRegex: /<span[^>]*class=\"[^\"]*company[^\"]*\"[^>]*>([^<]+)<\\/span>/gi,\n      linkRegex: /<a[^>]*href=\"([^\"]*offre[^\"]*)\"/gi\n    },\n    'indeed.com': {\n      titleRegex: /<h2[^>]*class=\"[^\"]*jobTitle[^\"]*\"[^>]*>.*?<span[^>]*>([^<]+)<\\/span>/gi,\n      companyRegex: /<span[^>]*class=\"[^\"]*companyName[^\"]*\"[^>]*>([^<]+)<\\/span>/gi,\n      linkRegex: /<a[^>]*href=\"(\\/viewjob[^\"]*)\"/gi\n    },\n    'pole-emploi.fr': {\n      titleRegex: /<h4[^>]*class=\"[^\"]*media-heading[^\"]*\"[^>]*>([^<]+)<\\/h4>/gi,\n      companyRegex: /<p[^>]*class=\"[^\"]*subtitle[^\"]*\"[^>]*>([^<]+)<\\/p>/gi,\n      linkRegex: /<a[^>]*href=\"([^\"]*offre[^\"]*)\"/gi\n    }\n  };\n\n  const siteKey = Object.keys(patterns).find(key => url.includes(key)) || 'default';\n  const pattern = patterns[siteKey] || patterns['indeed.com'];\n  \n  const offres = [];\n  let match;\n  \n  // Extraction des titres\n  const titres = [];\n  while ((match = pattern.titleRegex.exec(html)) !== null) {\n    titres.push(match[1].trim());\n    if (titres.length >= 20) break; // Limite \u00e0 20 offres par site\n  }\n  \n  // G\u00e9n\u00e9ration d'offres simul\u00e9es (\u00e0 remplacer par vraie extraction)\n  for (let i = 0; i < Math.min(titres.length, 5); i++) {\n    const titre = titres[i] || `Offre ${i + 1} - ${siteName}`;\n    \n    offres.push({\n      id: `${siteName}-${Date.now()}-${i}`,\n      title: titre,\n      company: `Entreprise ${i + 1}`,\n      description: `Description pour ${titre}. Alternance en cybers\u00e9curit\u00e9 avec formation et missions pratiques.`,\n      location: config.localisation,\n      source: siteName,\n      url: url,\n      date_scraped: new Date().toISOString(),\n      keywords: ['alternance', 'cybers\u00e9curit\u00e9', 's\u00e9curit\u00e9 informatique']\n    });\n  }\n  \n  return offres;\n}\n\n// Scraping de tous les sites\nconst promises = sites.map((url, index) => {\n  const siteName = extractSiteName(url);\n  return scraperSite(url, siteName);\n});\n\nfunction extractSiteName(url) {\n  if (url.includes('apec.fr')) return 'APEC';\n  if (url.includes('indeed.com')) return 'Indeed';\n  if (url.includes('pole-emploi.fr') || url.includes('labonnealternance')) return 'France Travail';\n  if (url.includes('walt.community')) return 'Walt';\n  if (url.includes('bloom-alternance.fr')) return 'Bloom Alternance';\n  return 'Site Inconnu';\n}\n\ntry {\n  const resultats = await Promise.all(promises);\n  \n  // Consolidation des offres\n  resultats.forEach(offres => {\n    offresCollectees.push(...offres);\n  });\n  \n  console.log(`\ud83c\udfaf === SCRAPING TERMIN\u00c9 ===`);\n  console.log(`\ud83d\udcca Total offres collect\u00e9es: ${offresCollectees.length}`);\n  \n  // D\u00e9doublonnage basique\n  const offresUniques = offresCollectees.filter((offre, index, self) => \n    index === self.findIndex(o => o.title === offre.title && o.company === offre.company)\n  );\n  \n  console.log(`\ud83e\uddf9 Apr\u00e8s d\u00e9doublonnage: ${offresUniques.length} offres`);\n  \n  return offresUniques.map(offre => ({ json: offre }));\n  \n} catch (error) {\n  console.log('\ud83d\udea8 Erreur globale scraping:', error.message);\n  \n  // Retour d'offres de test en cas d'erreur\n  const offresTest = [\n    {\n      id: 'test-1',\n      title: 'Alternant Cybers\u00e9curit\u00e9 - SOC Analyst',\n      company: 'SecureTech Solutions',\n      description: 'Rejoignez notre \u00e9quipe SOC pour une alternance en cybers\u00e9curit\u00e9. Missions: surveillance, analyse d\\'incidents, r\u00e9ponse aux menaces.',\n      location: 'Paris, France',\n      source: 'TEST',\n      url: 'https://example.com/offre1',\n      date_scraped: new Date().toISOString(),\n      keywords: ['alternance', 'cybers\u00e9curit\u00e9', 'SOC']\n    },\n    {\n      id: 'test-2',\n      title: 'Stage - Marketing Digital', \n      company: 'AgenceWeb',\n      description: 'Stage en marketing digital, cr\u00e9ation de contenu et gestion des r\u00e9seaux sociaux.',\n      location: 'Lyon, France',\n      source: 'TEST',\n      url: 'https://example.com/offre2',\n      date_scraped: new Date().toISOString(),\n      keywords: ['stage', 'marketing', 'digital']\n    },\n    {\n      id: 'test-3',\n      title: 'Alternant Pentesteur Junior',\n      company: 'CyberAudit Pro',\n      description: 'Alternance en tests d\\'intrusion et audit de s\u00e9curit\u00e9. Formation compl\u00e8te aux outils de pentest.',\n      location: 'Marseille, France', \n      source: 'TEST',\n      url: 'https://example.com/offre3',\n      date_scraped: new Date().toISOString(),\n      keywords: ['alternance', 'pentest', 's\u00e9curit\u00e9']\n    }\n  ];\n  \n  console.log('\ud83d\udd04 Utilisation des offres de test');\n  return offresTest.map(offre => ({ json: offre }));\n}"
      },
      "id": "scraper-node-003",
      "name": "\ud83d\udd77\ufe0f Scraper Multi-Sites",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        520,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// CLASSIFICATION MISTRAL LARGE - OPTIMIS\u00c9E POUR ALTERNANCES CYBERS\u00c9CURIT\u00c9\n\n// Configuration avec mod\u00e8le performant\nconst config = {\n  apiKey: process.env.MISTRAL_API_KEY || 'fe8GdBIIBwYk8Dj1GvclASPE3j0Zbt95',\n  apiUrl: 'https://api.mistral.ai/v1/chat/completions',\n  model: 'mistral-large-latest', // Mod\u00e8le le plus performant\n  temperature: 0.1,\n  max_tokens: 200\n};\n\nconst offre = $input.item.json;\n\nconsole.log(`\ud83e\udd16 === CLASSIFICATION MISTRAL LARGE: ${offre.title} ===`);\nconsole.log('\ud83c\udfe2 Entreprise:', offre.company);\nconsole.log('\ud83d\udccd Source:', offre.source);\nconsole.log('\ud83d\udd17 URL:', offre.url);\n\n// Prompt expert optimis\u00e9 pour la d\u00e9tection d'alternances cybers\u00e9curit\u00e9\nconst prompt = `Tu es un expert RH sp\u00e9cialis\u00e9 en cybers\u00e9curit\u00e9 et alternance.\n\nAnalyse cette offre d'emploi :\n\n**TITRE:** ${offre.title}\n**ENTREPRISE:** ${offre.company}\n**DESCRIPTION:** ${offre.description || 'Non sp\u00e9cifi\u00e9e'}\n**LOCALISATION:** ${offre.location}\n**MOTS-CL\u00c9S:** ${offre.keywords ? offre.keywords.join(', ') : 'Aucun'}\n\n**MISSION:** D\u00e9termine si cette offre correspond EXACTEMENT \u00e0 une ALTERNANCE en CYBERS\u00c9CURIT\u00c9.\n\n**CRIT\u00c8RES OBLIGATOIRES:**\n\n1. **TYPE DE CONTRAT:**\n   \u2705 ALTERNANCE (contrat d'apprentissage ou professionnalisation)\n   \u274c Stage, CDI, CDD, freelance, mission\n\n2. **DOMAINE CYBERS\u00c9CURIT\u00c9:**\n   \u2705 S\u00e9curit\u00e9 informatique, SOC, SIEM, pentest, audit s\u00e9curit\u00e9, forensic, GRC s\u00e9curit\u00e9\n   \u2705 Analyste s\u00e9curit\u00e9, ing\u00e9nieur cybers\u00e9curit\u00e9, consultant s\u00e9curit\u00e9\n   \u274c D\u00e9veloppement web, marketing, RH, comptabilit\u00e9\n\n3. **NIVEAU:**\n   \u2705 Junior, d\u00e9butant, \u00e9tudiant, formation\n   \u274c Senior, expert, 5+ ans d'exp\u00e9rience\n\n**ANALYSE:**\nExamine attentivement le titre et la description.\nRecherche les mots-cl\u00e9s sp\u00e9cifiques : \"alternance\", \"apprentissage\", \"cybers\u00e9curit\u00e9\", \"s\u00e9curit\u00e9 informatique\".\n\n**R\u00c9PONSE:**\nR\u00e9ponds UNIQUEMENT par:\n- **VALIDE** si l'offre respecte TOUS les crit\u00e8res\n- **INVALIDE** si un seul crit\u00e8re n'est pas respect\u00e9\n\nAjoute en une ligne la raison principale de ton choix.`;\n\n// Payload optimis\u00e9\nconst payload = {\n  model: config.model,\n  messages: [\n    {\n      role: \"system\",\n      content: \"Tu es un expert RH sp\u00e9cialis\u00e9 en cybers\u00e9curit\u00e9. Tu analyses les offres d'emploi avec une pr\u00e9cision chirurgicale pour identifier les vraies alternances en cybers\u00e9curit\u00e9.\"\n    },\n    {\n      role: \"user\", \n      content: prompt\n    }\n  ],\n  temperature: config.temperature,\n  max_tokens: config.max_tokens\n};\n\nconsole.log('\ud83d\udce6 Payload Mistral Large pr\u00e9par\u00e9');\nconsole.log('\ud83c\udfaf Mod\u00e8le utilis\u00e9:', config.model);\nconsole.log('\ud83d\udccf Taille prompt:', prompt.length, 'caract\u00e8res');\n\ntry {\n  // V\u00e9rification fetch\n  const fetchFn = typeof fetch !== 'undefined' ? fetch : \n                 (typeof globalThis.fetch !== 'undefined' ? globalThis.fetch : null);\n  \n  if (!fetchFn) {\n    throw new Error('Fetch API non disponible');\n  }\n\n  console.log('\ud83c\udf10 Appel API Mistral Large...');\n  \n  const response = await fetchFn(config.apiUrl, {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n      'Authorization': `Bearer ${config.apiKey}`\n    },\n    body: JSON.stringify(payload)\n  });\n\n  console.log('\ud83d\udcca Status HTTP:', response.status);\n\n  if (!response.ok) {\n    const errorText = await response.text();\n    console.log('\u274c Erreur API Mistral:', response.status, errorText);\n    \n    return {\n      json: {\n        ...offre,\n        mistral_response: 'ERREUR_API',\n        classification: 'ERREUR',\n        is_valid: false,\n        confidence: 0,\n        error: `HTTP ${response.status}: ${errorText}`,\n        model_used: config.model,\n        processed_at: new Date().toISOString()\n      }\n    };\n  }\n\n  const data = await response.json();\n  console.log('\ud83d\udce5 R\u00e9ponse Mistral Large re\u00e7ue');\n\n  if (data?.choices?.[0]?.message?.content) {\n    const content = data.choices[0].message.content.trim();\n    \n    console.log('\u2705 === MISTRAL LARGE SUCCESS ===');\n    console.log('\ud83d\udcdd R\u00e9ponse compl\u00e8te:', content);\n    \n    // Analyse intelligente de la r\u00e9ponse\n    const contentUpper = content.toUpperCase();\n    let classification, isValid, confidence, raison;\n    \n    // Extraction de la raison\n    const lignes = content.split('\\n').filter(l => l.trim());\n    raison = lignes.length > 1 ? lignes[1] : 'Analyse automatique';\n    \n    if (contentUpper.includes('VALIDE') && !contentUpper.includes('INVALIDE')) {\n      classification = 'VALIDE';\n      isValid = true;\n      confidence = 0.95; // Confiance \u00e9lev\u00e9e avec Mistral Large\n      console.log('\u2705 OFFRE VALID\u00c9E - Alternance cybers\u00e9curit\u00e9 confirm\u00e9e');\n    } else if (contentUpper.includes('INVALIDE')) {\n      classification = 'INVALIDE';\n      isValid = false;\n      confidence = 0.95;\n      console.log('\u274c OFFRE REJET\u00c9E - Ne correspond pas aux crit\u00e8res');\n    } else {\n      classification = 'INCERTAIN';\n      isValid = false;\n      confidence = 0.3;\n      console.log('\u26a0\ufe0f R\u00e9ponse ambigu\u00eb de Mistral Large');\n    }\n\n    console.log('\ud83c\udfaf Classification finale:', classification);\n    console.log('\ud83d\udcad Raison:', raison);\n    console.log('\ud83d\udcca Usage tokens:', JSON.stringify(data.usage || {}));\n\n    return {\n      json: {\n        ...offre,\n        mistral_response: content,\n        classification: classification,\n        is_valid: isValid,\n        confidence: confidence,\n        raison: raison,\n        model_used: data.model || config.model,\n        usage: data.usage || {},\n        processed_at: new Date().toISOString(),\n        method: 'mistral_large_expert'\n      }\n    };\n\n  } else {\n    console.log('\u274c Structure r\u00e9ponse Mistral invalide');\n    \n    return {\n      json: {\n        ...offre,\n        mistral_response: 'STRUCTURE_INVALIDE',\n        classification: 'ERREUR',\n        is_valid: false,\n        confidence: 0,\n        error: 'Structure r\u00e9ponse Mistral invalide',\n        model_used: config.model,\n        processed_at: new Date().toISOString()\n      }\n    };\n  }\n\n} catch (error) {\n  console.log('\ud83d\udea8 === ERREUR CRITIQUE ===');\n  console.log('Type:', error.constructor.name);\n  console.log('Message:', error.message);\n  \n  return {\n    json: {\n      ...offre,\n      mistral_response: 'ERREUR_RESEAU',\n      classification: 'ERREUR',\n      is_valid: false,\n      confidence: 0,\n      error: `${error.constructor.name}: ${error.message}`,\n      model_used: config.model,\n      processed_at: new Date().toISOString()\n    }\n  };\n}"
      },
      "id": "mistral-node-004",
      "name": "\ud83e\udde0 Classification Mistral Large",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        740,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "condition-valid-id",
              "leftValue": "={{ $json.classification }}",
              "rightValue": "VALIDE",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "filter-node-005",
      "name": "\u2705 Offre Valide ?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        960,
        300
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "valid-action-id",
              "name": "action",
              "value": "OFFRE_ALTERNANCE_ACCEPTEE",
              "type": "string"
            },
            {
              "id": "valid-message-id",
              "name": "message",
              "value": "\ud83c\udfaf Alternance cybers\u00e9curit\u00e9 VALID\u00c9E !",
              "type": "string"
            },
            {
              "id": "valid-score-id",
              "name": "score_pertinence",
              "value": "={{ $json.confidence }}",
              "type": "number"
            }
          ]
        },
        "options": {}
      },
      "id": "accept-node-006",
      "name": "\ud83c\udfaf Traitement Offre Valide",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1180,
        180
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "invalid-action-id",
              "name": "action",
              "value": "OFFRE_REJETEE",
              "type": "string"
            },
            {
              "id": "invalid-message-id",
              "name": "message",
              "value": "\u274c Ne correspond pas: {{ $json.raison || 'Crit\u00e8res non respect\u00e9s' }}",
              "type": "string"
            },
            {
              "id": "invalid-reason-id",
              "name": "raison_rejet",
              "value": "={{ $json.raison }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "reject-node-007",
      "name": "\u274c Traitement Offre Invalide",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1180,
        420
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "notify-condition-id",
              "leftValue": "={{ $json.action }}",
              "rightValue": "OFFRE_ALTERNANCE_ACCEPTEE",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "notify-filter-008",
      "name": "\ud83d\udd14 Notification ?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1400,
        180
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "notif-id",
              "name": "notification",
              "value": "\ud83d\udea8 ALERTE: Nouvelle alternance cybers\u00e9curit\u00e9 trouv\u00e9e !\n\n\ud83d\udccb Titre: {{ $json.title }}\n\ud83c\udfe2 Entreprise: {{ $json.company }}\n\ud83d\udccd Lieu: {{ $json.location }}\n\ud83c\udf10 Source: {{ $json.source }}\n\ud83c\udfaf Score: {{ $json.score_pertinence }}\n\ud83d\udd17 Lien: {{ $json.url }}\n\n\u2705 Valid\u00e9e par Mistral Large",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "notification-009",
      "name": "\ud83d\udcec Pr\u00e9parer Notification",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1620,
        180
      ]
    }
  ],
  "connections": {
    "\ud83d\ude80 Start": {
      "main": [
        [
          {
            "node": "\u2699\ufe0f Configuration Recherche",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2699\ufe0f Configuration Recherche": {
      "main": [
        [
          {
            "node": "\ud83d\udd77\ufe0f Scraper Multi-Sites",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd77\ufe0f Scraper Multi-Sites": {
      "main": [
        [
          {
            "node": "\ud83e\udde0 Classification Mistral Large",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\udde0 Classification Mistral Large": {
      "main": [
        [
          {
            "node": "\u2705 Offre Valide ?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2705 Offre Valide ?": {
      "main": [
        [
          {
            "node": "\ud83c\udfaf Traitement Offre Valide",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\u274c Traitement Offre Invalide",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83c\udfaf Traitement Offre Valide": {
      "main": [
        [
          {
            "node": "\ud83d\udd14 Notification ?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd14 Notification ?": {
      "main": [
        [
          {
            "node": "\ud83d\udcec Pr\u00e9parer Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "workflow-alternance-v1",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "id": "alternance-cybersec-workflow",
  "tags": [
    "alternance",
    "cybers\u00e9curit\u00e9",
    "scraping",
    "mistral"
  ]
}