[Modèle] Établir un contact (paramètres)

Intermédiaire

Ceci est uncontenant 14 nœuds.Utilise principalement des nœuds comme Code, GoogleDrive, GoogleSheets, OpenAi, GoogleSheetsTrigger. Extraire des données de profils LinkedIn et générer un contenu de suivi avec Vision AI et Google Forms

Prérequis
  • Informations d'identification Google Drive API
  • Informations d'identification Google Sheets API
  • Clé API OpenAI

Catégorie

-
Aperçu du workflow
Visualisation des connexions entre les nœuds, avec support du zoom et du déplacement
Exporter le workflow
Copiez la configuration JSON suivante dans n8n pour importer et utiliser ce workflow
{
  "id": "RWghkZDBJi9mm0Bq",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "[Template] Building Relationships (param)",
  "tags": [],
  "nodes": [
    {
      "id": "55136a9d-4d1e-455d-8b26-f449c28da2bb",
      "name": "déclencheur : soumission de formulaire",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "position": [
        -336,
        0
      ],
      "parameters": {
        "event": "rowAdded",
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "sheetName": "={{ $env.SHEETS_TAB || 'crm' }}",
        "documentId": "={{ $env.SHEETS_ID }}"
      },
      "typeVersion": 1
    },
    {
      "id": "9e262787-c907-4bf7-b8e4-2867ffe2946f",
      "name": "capture d'écran",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        -128,
        0
      ],
      "parameters": {
        "fileId": "={{ (function () { const col = $env.COL_DRIVE_URL || 'Upload a screenshot or image of the person'; const s = $json[col] || ''; const m = s.match(/(?:\\/file\\/d\\/|\\/d\\/|open\\?id=|uc\\?id=|thumbnail\\?id=|id=)([-\\w]{10,})/); return m ? m[1] : ''; })() }}",
        "options": {
          "binaryProperty": "data"
        },
        "operation": "download"
      },
      "typeVersion": 3
    },
    {
      "id": "6219058a-3a24-48d7-b2a9-cf861a6ae74b",
      "name": "Analyser l'image",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        112,
        0
      ],
      "parameters": {
        "text": "You extract structured data from images of profiles, email signatures, business cards, event badges, resumes, or screenshots.\n\nRules:\n- OUTPUT STRICT JSON ONLY. No markdown, no prose, no comments.\n- Return exactly these 5 keys; if a field isn’t clearly present, use null.\n- Prefer the primary/most prominent person if multiple appear.\n- Normalize whitespace and title-case personal names; keep original casing for company/university.\n- Keep description to 1–2 sentences summarizing what’s visible (role, focus, notable lines). Do not invent info.\n\nReturn exactly:\n{\n  \"name\": string|null,\n  \"job_title\": string|null,\n  \"company\": string|null,\n  \"university\": string|null,\n  \"description\": string|null\n}",
        "modelId": "={{ $env.OPENAI_VISION_MODEL || 'chatgpt-4o-latest' }}",
        "options": {},
        "resource": "image",
        "inputType": "binary",
        "operation": "analyze",
        "binaryPropertyName": "data"
      },
      "typeVersion": 1.8
    },
    {
      "id": "1fc377bc-18a7-4fe1-897f-4a0a8fcb9bf1",
      "name": "structurer les informations",
      "type": "n8n-nodes-base.code",
      "position": [
        352,
        0
      ],
      "parameters": {
        "jsCode": "// n8n Code node (JavaScript). Mode: \"Run Once for Each Item\"\nfunction titleCaseName(s) {\n  if (!s || typeof s !== 'string') return s;\n  return s\n    .trim()\n    .split(/\\s+/)\n    .map(w => w[0] ? (w[0].toUpperCase() + w.slice(1).toLowerCase()) : w)\n    .join(' ');\n}\n\nfunction clean(v) {\n  if (v === undefined || v === null) return null;\n  const s = String(v).trim();\n  return s === '' ? null : s;\n}\n\nconst out = [];\n\nfor (const item of items) {\n  // 1) Get raw model output (handles various OpenAI node shapes)\n  let raw =\n    item.json?.text ??                           // common \"Simplify Output\" field\n    item.json?.content ??                        // some OpenAI nodes\n    item.json?.response ??                       // alt\n    item.json?.choices?.[0]?.message?.content ?? // raw OpenAI\n    item.json;                                   // last resort\n\n  // 2) Parse to object (strip code fences, smart quotes, double-parse fallback)\n  let obj = raw;\n  if (typeof raw === 'string') {\n    let s = raw.trim().replace(/^```(?:json)?\\s*|\\s*```$/g, '');\n    s = s.replace(/[\\u201C\\u201D]/g, '\"').replace(/[\\u2018\\u2019]/g, \"'\");\n    try { obj = JSON.parse(s); }\n    catch {\n      obj = JSON.parse(JSON.parse(s)); // handles quoted-JSON-in-a-string\n    }\n  }\n\n  // 3) Accept common alias keys from the model, then normalize\n  const aliases = {\n    name: ['name', 'full_name', 'person', 'candidate'],\n    job_title: ['job_title', 'title', 'role', 'position', 'headline'],\n    company: ['company', 'org', 'employer', 'organization'],\n    location: ['location', 'city', 'based_in', 'place'],\n    university: ['university', 'school', 'alma_mater', 'education'],\n    description: ['description', 'summary', 'about', 'bio']\n  };\n\n  const pick = (o, keys) => {\n    for (const k of keys) if (o && o[k] != null) return o[k];\n    return null;\n  };\n\n  const result = {\n    name:        titleCaseName(clean(pick(obj, aliases.name))),\n    job_title:   clean(pick(obj, aliases.job_title)),\n    company:     clean(pick(obj, aliases.company)),\n    location:    clean(pick(obj, aliases.location)),\n    university:  clean(pick(obj, aliases.university)),\n    description: clean(pick(obj, aliases.description))\n  };\n\n  // 4) Optional: trim description length\n  if (result.description && result.description.length > 300) {\n    result.description = result.description.slice(0, 297) + '...';\n  }\n\n  out.push({ json: result });\n}\n\nreturn out;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "5346ea2f-b3e0-4056-9026-30dc3ad5320f",
      "name": "mettre à jour le CRM",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        560,
        0
      ],
      "parameters": {
        "columns": {
          "value": {
            "company": "={{ $json.company }}",
            "location": "={{ $json.location }}",
            "full name": "={{ $json.name }}",
            "job title": "={{ $json.job_title }}",
            "university": "={{ $json.university }}",
            "description": "={{ $json.description }}",
            "Upload a screenshot or image of the person": "={{ $('get screenshot').item.json[$env.COL_DRIVE_URL || 'Upload a screenshot or image of the person'] }}"
          },
          "schema": [
            {
              "id": "Timestamp",
              "type": "string",
              "display": true,
              "removed": true,
              "displayName": "Timestamp",
              "canBeUsedToMatch": true
            },
            {
              "id": "Upload a screenshot or image of the person",
              "type": "string",
              "display": true,
              "displayName": "Upload a screenshot or image of the person",
              "canBeUsedToMatch": true
            },
            {
              "id": "Quick notes about the person",
              "type": "string",
              "display": true,
              "removed": true,
              "displayName": "Quick notes about the person",
              "canBeUsedToMatch": true
            },
            {
              "id": "full name",
              "type": "string",
              "display": true,
              "displayName": "full name",
              "canBeUsedToMatch": true
            },
            {
              "id": "company",
              "type": "string",
              "display": true,
              "displayName": "company",
              "canBeUsedToMatch": true
            },
            {
              "id": "job title",
              "type": "string",
              "display": true,
              "displayName": "job title",
              "canBeUsedToMatch": true
            },
            {
              "id": "location",
              "type": "string",
              "display": true,
              "displayName": "location",
              "canBeUsedToMatch": true
            },
            {
              "id": "description",
              "type": "string",
              "display": true,
              "displayName": "description",
              "canBeUsedToMatch": true
            },
            {
              "id": "university",
              "type": "string",
              "display": true,
              "displayName": "university",
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Upload a screenshot or image of the person"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": "={{ $env.SHEETS_TAB || 'crm' }}",
        "documentId": "={{ $env.SHEETS_ID }}"
      },
      "typeVersion": 4.7
    },
    {
      "id": "b410e112-ca36-4d17-96c8-777fbea7d3c0",
      "name": "Modèle de message",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        768,
        0
      ],
      "parameters": {
        "modelId": "={{ $env.OPENAI_TEXT_MODEL || 'gpt-4o-mini' }}",
        "options": {
          "temperature": 0.2
        },
        "messages": {
          "values": [
            {
              "content": "=NOTES (must use): {{ $('trigger: form submission').item.json[$env.COL_NOTES || 'Quick notes about the person'] }}\n\nFIELDS (use only if present; never guess):\nname: {{ $json.name || $json[\"full name\"] }}\ntitle: {{ $json.job_title || $json[\"job title\"] }}\ncompany: {{ $json.company }}\nlocation: {{ $json.location }}\nuniversity: {{ $json.university }}\ndescription: {{ $json.description }}\n\nTASK\nWrite ONE LinkedIn DM that:\n- Cites exactly ONE concrete detail from NOTES.\n- Optionally weaves in title/company/location if present.\n- Makes ONE light ask.\n- Ends with a question.\nPlain text only, ≤450 chars, no added facts beyond NOTES/FIELDS."
            },
            {
              "role": "system",
              "content": "You write short LinkedIn follow-ups grounded in structured fields.\n\nGROUNDING & SAFETY\n- ALWAYS anchor on NOTES. If a detail is not in NOTES or the provided fields, OMIT it.\n- NEVER invent, infer, or generalize.\n- If a field is empty or unclear, ignore it—don’t guess.\n- Keep claims strictly to what’s provided.\n\nSTYLE\n- Plain text only (no emojis, bullets, or brackets). ≤ 450 characters.\n- Friendly, crisp, professional. Reference ONE concrete detail from NOTES.\n- Suggest ONE light next step (share resource / quick chat / intro).\n- End with ONE question.\n\nCONTENT RULES\n- If name is available, greet by first name only.\n- Use title/company/location only if present and useful.\n- Do not mention university unless present and relevant."
            }
          ]
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "2c060fb9-5a0d-45a4-8b72-623c2d64be88",
      "name": "mettre à jour le CRM avec le message",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1120,
        0
      ],
      "parameters": {
        "columns": {
          "value": {
            "follow-up message": "={{ $json.message?.content || $json.text || $json.content }}",
            "Upload a screenshot or image of the person": "={{ $('get screenshot').item.json[$env.COL_DRIVE_URL || 'Upload a screenshot or image of the person'] }}"
          },
          "schema": [
            {
              "id": "Timestamp",
              "type": "string",
              "display": true,
              "removed": true,
              "displayName": "Timestamp",
              "canBeUsedToMatch": true
            },
            {
              "id": "Upload a screenshot or image of the person",
              "type": "string",
              "display": true,
              "displayName": "Upload a screenshot or image of the person",
              "canBeUsedToMatch": true
            },
            {
              "id": "Quick notes about the person",
              "type": "string",
              "display": true,
              "removed": true,
              "displayName": "Quick notes about the person",
              "canBeUsedToMatch": true
            },
            {
              "id": "full name",
              "type": "string",
              "display": true,
              "removed": true,
              "displayName": "full name",
              "canBeUsedToMatch": true
            },
            {
              "id": "company",
              "type": "string",
              "display": true,
              "removed": true,
              "displayName": "company",
              "canBeUsedToMatch": true
            },
            {
              "id": "job title",
              "type": "string",
              "display": true,
              "removed": true,
              "displayName": "job title",
              "displayNameSrc": "job title",
              "canBeUsedToMatch": true
            },
            {
              "id": "location",
              "type": "string",
              "display": true,
              "removed": true,
              "displayName": "location",
              "canBeUsedToMatch": true
            },
            {
              "id": "description",
              "type": "string",
              "display": true,
              "removed": true,
              "displayName": "description",
              "canBeUsedToMatch": true
            },
            {
              "id": "university",
              "type": "string",
              "display": true,
              "removed": true,
              "displayName": "university",
              "canBeUsedToMatch": true
            },
            {
              "id": "follow-up message",
              "type": "string",
              "display": true,
              "displayName": "follow-up message",
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Upload a screenshot or image of the person"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": "={{ $env.SHEETS_TAB || 'crm' }}",
        "documentId": "={{ $env.SHEETS_ID }}"
      },
      "typeVersion": 4.7
    },
    {
      "id": "15d58a4e-6c00-4e4a-afda-09f82722eb30",
      "name": "Note adhésive",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -368,
        -208
      ],
      "parameters": {
        "width": 192,
        "content": "Trigger:\nWhen a 2-field only Google Form is submitted with a LinkedIn header screenshot + quick personal notes. "
      },
      "typeVersion": 1
    },
    {
      "id": "9f91086d-210b-4fc8-89e4-415375dafb37",
      "name": "Note adhésive1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -128,
        -208
      ],
      "parameters": {
        "width": 150,
        "content": "This step gets the new screenshot to parse information"
      },
      "typeVersion": 1
    },
    {
      "id": "c24d446a-3d2d-4639-99f8-05ac599900ed",
      "name": "Note adhésive2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        80,
        -208
      ],
      "parameters": {
        "width": 150,
        "content": "Open AI's vision model analyses the screenshot to extract info"
      },
      "typeVersion": 1
    },
    {
      "id": "5102f336-add5-4992-a9fa-2542021f7558",
      "name": "Note adhésive3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        304,
        -208
      ],
      "parameters": {
        "width": 150,
        "content": "Extracted information is structured "
      },
      "typeVersion": 1
    },
    {
      "id": "d54eb503-b086-431c-b483-fa9c67701c88",
      "name": "Note adhésive4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        528,
        -208
      ],
      "parameters": {
        "width": 150,
        "content": "Google sheet with form submission info is updated with added fields"
      },
      "typeVersion": 1
    },
    {
      "id": "6a945f3a-6926-4f6d-9d3d-d9415af4355e",
      "name": "Note adhésive5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        784,
        -208
      ],
      "parameters": {
        "width": 150,
        "content": "With all this info AND your original notes, Open AI generates a message"
      },
      "typeVersion": 1
    },
    {
      "id": "dbd68a25-b501-4f06-a1ea-dc746fced6f3",
      "name": "Note adhésive6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1056,
        -208
      ],
      "parameters": {
        "width": 150,
        "content": "crm updated with message"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "callerPolicy": "workflowsFromSameOwner",
    "executionOrder": "v1"
  },
  "versionId": "5bc78610-e614-4340-8703-a7e7bcf2d274",
  "connections": {
    "5346ea2f-b3e0-4056-9026-30dc3ad5320f": {
      "main": [
        [
          {
            "node": "b410e112-ca36-4d17-96c8-777fbea7d3c0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "6219058a-3a24-48d7-b2a9-cf861a6ae74b": {
      "main": [
        [
          {
            "node": "1fc377bc-18a7-4fe1-897f-4a0a8fcb9bf1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "9e262787-c907-4bf7-b8e4-2867ffe2946f": {
      "main": [
        [
          {
            "node": "6219058a-3a24-48d7-b2a9-cf861a6ae74b",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1fc377bc-18a7-4fe1-897f-4a0a8fcb9bf1": {
      "main": [
        [
          {
            "node": "5346ea2f-b3e0-4056-9026-30dc3ad5320f",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "b410e112-ca36-4d17-96c8-777fbea7d3c0": {
      "main": [
        [
          {
            "node": "2c060fb9-5a0d-45a4-8b72-623c2d64be88",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "55136a9d-4d1e-455d-8b26-f449c28da2bb": {
      "main": [
        [
          {
            "node": "9e262787-c907-4bf7-b8e4-2867ffe2946f",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Foire aux questions

Comment utiliser ce workflow ?

Copiez le code de configuration JSON ci-dessus, créez un nouveau workflow dans votre instance n8n et sélectionnez "Importer depuis le JSON", collez la configuration et modifiez les paramètres d'authentification selon vos besoins.

Dans quelles scénarios ce workflow est-il adapté ?

Intermédiaire

Est-ce payant ?

Ce workflow est entièrement gratuit et peut être utilisé directement. Veuillez noter que les services tiers utilisés dans le workflow (comme l'API OpenAI) peuvent nécessiter un paiement de votre part.

Informations sur le workflow
Niveau de difficulté
Intermédiaire
Nombre de nœuds14
Catégorie-
Types de nœuds6
Description de la difficulté

Adapté aux utilisateurs expérimentés, avec des workflows de complexité moyenne contenant 6-15 nœuds

Liens externes
Voir sur n8n.io

Partager ce workflow

Catégories

Catégories: 34