Traitement automatisé des factures en utilisant OCR, GPT-4 et la création d'opportunités Salesforce
Ceci est unInvoice Processing, AI Summarizationworkflow d'automatisation du domainecontenant 23 nœuds.Utilise principalement des nœuds comme Code, Salesforce, GoogleDrive, HttpRequest, ExtractFromFile. Automatisation du traitement des factures avec OCR, GPT-4 et la création d'opportunités Salesforce
- •Informations d'identification OAuth Salesforce
- •Informations d'identification Google Drive API
- •Peut nécessiter les informations d'identification d'authentification de l'API cible
- •Clé API OpenAI
Nœuds utilisés (23)
Catégorie
{
"meta": {
"instanceId": "9a562c06a632241f66aadd52a495ad98e76b760ef5cfce9c319a4759c47cd94e",
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "c87ed7b7-b77d-4236-999e-afefe9064033",
"name": "Note adhésive",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2640,
-656
],
"parameters": {
"width": 464,
"height": 912,
"content": "# PDF Invoice Extractor (AI) to Salesforce Opportunity\n\nWhat it does:\nWatch a Drive folder → OCR PDF → extract invoices (JSON) via LLM → mirror file to OneDrive → upsert Buyer to Salesforce → create Opportunity → add Opportunity Line Items from Pricebook.\n\n\n## Flow\n 1)Drive Trigger → 2) Download → 3) Extract → 4) → 5) Upsert Account → 6) Create Opportunity → 7) Build SOQL → 8) Query PBE → 9) Build OLI → 10) Create OLIs -> 11) OneDrive \n\n\n## Outputs\n- LLM: normalized JSON array of invoices\n- Salesforce: Account (buyer), Opportunity, Line Items\n- OneDrive: original PDF archived\n\n## Quick Troubleshooting\n- Empty OCR → enable OCR in step 3\n- Non-JSON → enforce jsonOutput:true + strict prompt\n- Missing PBE → verify ProductCode matches Salesforce & Pricebook2Id\n- Auth errors → re-connect Google/OneDrive/Salesforce/OpenAI\n\n## Notes\n- Keep numbers as JSON numbers; dates YYYY-MM-DD\n- If fields missing, return null; log warnings in parsing.warnings\n- Replace hardcoded IDs (Pricebook2Id, OneDrive Folder, SF domain) with your values\n"
},
"typeVersion": 1
},
{
"id": "e7665458-f2f8-4ad1-b318-1fbe4e3ff464",
"name": "Extraire d'un fichier",
"type": "n8n-nodes-base.extractFromFile",
"position": [
-1456,
-144
],
"parameters": {
"options": {},
"operation": "pdf"
},
"typeVersion": 1
},
{
"id": "1a593bd5-922f-4f0d-9ddb-0220ed3be3cb",
"name": "Déclencheur Google Drive",
"type": "n8n-nodes-base.googleDriveTrigger",
"position": [
-1952,
-144
],
"parameters": {
"event": "fileCreated",
"options": {},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"triggerOn": "specificFolder",
"folderToWatch": {
"__rl": true,
"mode": "list",
"value": "125tDoA_XgewTZetNg4UhYoYuoomIKLuP",
"cachedResultUrl": "https://drive.google.com/drive/folders/125tDoA_XgewTZetNg4UhYoYuoomIKLuP",
"cachedResultName": "Sample"
}
},
"credentials": {
"googleDriveOAuth2Api": {
"id": "CfyGMYYwObyuC3Q6",
"name": "Google Drive account"
}
},
"typeVersion": 1
},
{
"id": "ce439fd0-ffcf-4891-a861-61fa62e57a38",
"name": "Envoyer un message à un modèle",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
-1264,
-144
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1",
"cachedResultName": "GPT-4.1"
},
"options": {},
"messages": {
"values": [
{
"role": "system",
"content": "=Role: You are a meticulous extractor. Given raw OCR text that may contain one or many tax invoices, return a normalized JSON array. Do not invent data. If a field is missing, use null (or []).\n\nInput\n- batch_text: {{ $json.text }}\n\nOutput\n- Return only a JSON array. Each element is one invoice with the schema:\n\n[\n {\n \"seller\": {\n \"name\": \"string|null\",\n \"tax_id\": \"string|null\",\n \"address\": \"string|null\",\n \"phone\": \"string|null\",\n \"email\": \"string|null\",\n \"bank_account\": \"string|null\",\n \"bank_name\": \"string|null\",\n \"bank_branch\": \"string|null\"\n },\n \"buyer\": {\n \"name\": \"string|null\",\n \"tax_id\": \"string|null\",\n \"address\": \"string|null\",\n \"phone\": \"string|null\",\n \"buyer_representative\": \"string|null\"\n },\n \"invoice\": {\n \"type\": \"string|null\",\n \"issue_date\": \"YYYY-MM-DD|null\",\n \"code\": \"string|null\",\n \"symbol\": \"string|null\",\n \"number\": \"string|null\",\n \"payment_method\": \"string|null\",\n \"currency\": \"string\",\n \"lookup_url\": \"string|null\",\n \"lookup_code\": \"string|null\"\n },\n \"products\": [\n {\n \"name\": \"string\",\n \"description\": \"string|null\",\n \"unit\": \"string|null\",\n \"quantity\": \"number\",\n \"unit_price\": \"number\",\n \"discount_percent\": \"number|null\",\n \"discount_amount\": \"number|null\",\n \"subtotal_excl_vat\": \"number\",\n \"tax_rate\": \"number|null\",\n \"tax_amount\": \"number|null\",\n \"line_total\": \"number|null\"\n }\n ],\n \"summary\": {\n \"total_discount\": \"number|null\",\n \"subtotal_excl_vat\": \"number|null\",\n \"vat_rate\": \"number|null\",\n \"vat_amount\": \"number|null\",\n \"grand_total\": \"number|null\",\n \"amount_in_words\": \"string|null\"\n },\n \"software\": {\n \"issuer\": \"string|null\",\n \"issuer_tax_id\": \"string|null\",\n \"notes\": \"string|null\"\n },\n \"parsing\": {\n \"warnings\": [\"string\"],\n \"assumptions\": [\"string\"]\n }\n }\n]\n\nLocale & normalization rules\n1) Currency & numbers:\n - Detect currency (e.g., USD, EUR, GBP, VND, ₫, $, €).\n - Remove thousands separators (., , or space).\n - Interpret decimals by locale (e.g., “1,234.56” vs “1.234,56”).\n - Use integers for zero-decimal currencies; otherwise decimals. If uncertain, keep decimal and add a warning.\n\n2) Dates:\n - Normalize to YYYY-MM-DD from formats like dd/mm/yyyy, mm-dd-yyyy, yyyy.mm.dd, or “dd Month yyyy”.\n - If ambiguous (e.g., 03/04/2024), infer from locale cues (currency/language). If still uncertain, set null and add a warning.\n\n3) Line wrapping:\n - Merge wrapped product description lines until quantity/unit/price/tax columns or totals appear.\n\n4) Discounts:\n - If only percent or amount present, compute the other when possible (round to nearest minor unit).\n - If discount appears as a negative line, treat as discount_amount.\n\n5) Per-line taxes:\n - Capture per-line Tax/VAT rate/amount if shown; otherwise set products[*].tax_rate and tax_amount = null and rely on summary vat_rate.\n\n6) Totals cross-check:\n - sum(products.subtotal_excl_vat) ≈ summary.subtotal_excl_vat (±1 minor unit)\n - summary.vat_amount ≈ round(subtotal_excl_vat * vat_rate/100)\n - grand_total = subtotal_excl_vat + vat_amount\n - If mismatch, keep parsed figures and append a parsing.warnings note.\n\nMulti-invoice splitting (critical)\n- Split batch_text into invoice blocks using anchors; each block = one invoice.\n\nPrimary anchors:\n- “TAX INVOICE”, “INVOICE”, “VAT INVOICE”, or similar header; OR\n- A cluster containing both “Serial/Symbol:” (or “Series:”) and “No./Number:” within ~10 lines.\n\nSecondary boundaries (helpful, not required):\n- “Tax Authority Code / QR / Validation Code” near header\n- “Amount in words:” or “Total amount payable:” near the end\n- Software footers (e.g., “Issued by … software”)\n\nHeuristic:\n- Start a new block when a header reappears or when “Series/Symbol:” and “No./Number:” occur after a prior “Total amount payable”.\n\nExtraction steps (per invoice block)\n1) Seller/Buyer:\n - Anchors: Company/Organization, Tax ID/VAT No., Address, Phone, Email, Bank Account/IBAN, Bank Name, Branch.\n - Buyer: Customer/Buyer Name, Tax ID (if any), Address, Phone, Buyer Representative/Contact.\n\n2) Invoice header:\n - Type, Issue date, Tax authority code (if any), Code (internal), Symbol/Series, Number, Payment method, Currency, Lookup URL/Code (QR/portal).\n\n3) Items table:\n - Detect headers like: No., Item/Service, Unit, Quantity, Unit Price, Amount, Discount %, Discount Amount, Net before VAT, Tax/VAT, Line Total.\n - Rebuild wrapped descriptions; keep units as-is if unsure.\n\n4) Summary:\n - Total discount, Subtotal excl. VAT, VAT rate, VAT amount, Grand total/Total payable, Amount in words, Lookup URL/code (if present).\n\n5) Software/Notes:\n - E-invoicing platform or software issuer (name & tax ID if shown) and any footer notes.\n\n6) Validate & warn:\n - Run cross-checks; record anomalies and assumptions (e.g., inferred decimal separator, currency, ambiguous date).\n\nStrict formatting\n- Output only the JSON array (no extra text).\n- All numeric fields must be JSON numbers.\n- Use UTF-8.\n- Do not guess missing data.\n\nAnti-hallucination\n- If a field is not explicitly present or reliably derivable, return null.\n- If per-line VAT isn’t printed, set products[*].tax_rate and tax_amount to null and compute only at summary level.\n- If headers are partially missing due to OCR, infer columns from value patterns (units, prices \\d[\\d.,]*, discounts %, etc.). If uncertain, add a warning.\n"
},
{}
]
},
"jsonOutput": true
},
"credentials": {
"openAiApi": {
"id": "ciKVFfcmd36noEnL",
"name": "OpenAi account"
}
},
"typeVersion": 1.8
},
{
"id": "053cdfcf-65f6-4ec6-90fa-090a08da31c8",
"name": "Télécharger un fichier depuis Google",
"type": "n8n-nodes-base.googleDrive",
"position": [
-1712,
-144
],
"parameters": {
"fileId": {
"__rl": true,
"mode": "id",
"value": "={{ $json.id }}"
},
"options": {},
"operation": "download"
},
"credentials": {
"googleDriveOAuth2Api": {
"id": "CfyGMYYwObyuC3Q6",
"name": "Google Drive account"
}
},
"typeVersion": 3
},
{
"id": "157f5cdc-de47-4f90-96ea-202636ac5016",
"name": "Mettre à jour un fichier vers OneDrive",
"type": "n8n-nodes-base.microsoftOneDrive",
"position": [
-1424,
32
],
"parameters": {
"fileName": "={{ $json.name }}",
"parentId": "folder_id_onedrive",
"binaryData": true
},
"credentials": {
"microsoftOneDriveOAuth2Api": {
"id": "bvhIzbOLKeAfHCQc",
"name": "Microsoft Drive account"
}
},
"typeVersion": 1
},
{
"id": "80f186d6-499e-4da2-a49e-a503de546be4",
"name": "Créer une opportunité",
"type": "n8n-nodes-base.salesforce",
"position": [
-656,
-144
],
"parameters": {
"name": "={{ $('Message a model').item.json.message.content.invoice.code }}",
"resource": "opportunity",
"closeDate": "={{ $('Message a model').item.json.message.content.invoice.issue_date }}",
"stageName": "Closed Won",
"additionalFields": {
"amount": "={{ $('Message a model').item.json.message.content.summary.grand_total }}",
"accountId": "={{ $json.id }}"
}
},
"credentials": {
"salesforceOAuth2Api": {
"id": "LDbxyOy7NMH6fin9",
"name": "Salesforce account 2"
}
},
"typeVersion": 1
},
{
"id": "9f06013f-ff72-4f08-b72e-4b42931f393f",
"name": "Créer ou mettre à jour un compte",
"type": "n8n-nodes-base.salesforce",
"position": [
-928,
-144
],
"parameters": {
"name": "={{ $json.message.content.buyer.name }}",
"resource": "account",
"operation": "upsert",
"externalId": "tax_id__c",
"externalIdValue": "={{ $json.message.content.buyer.tax_id }}",
"additionalFields": {}
},
"credentials": {
"salesforceOAuth2Api": {
"id": "LDbxyOy7NMH6fin9",
"name": "Salesforce account 2"
}
},
"typeVersion": 1
},
{
"id": "70336cbd-460f-4396-964c-196baf054d8f",
"name": "Construire SOQL",
"type": "n8n-nodes-base.code",
"position": [
-352,
-144
],
"parameters": {
"jsCode": "//const { pricebook2Id, lines } = $json;\n\nconst pricebook2Id = '01sxxxxxxxxxxxxxxx'; // replace your pricebookID\nconst lines = $('Message a model').first().json.message.content.products; \n\nconst codes = [\n ...new Set(\n (lines || [])\n .map((l) => String(l.name || \"\").trim())\n .filter(Boolean)\n ),\n];\n\nif (codes.length === 0) {\n throw new Error(\"No product codes found in lines[].name\");\n}\n\n// Escape single quotes for SOQL\nconst soqlList = codes\n .map((c) => `'${c.replace(/'/g, \"\\\\'\")}'`)\n .join(\",\");\n\nconst soql = `\n SELECT Id, Product2Id, Product2.ProductCode\n FROM PricebookEntry\n WHERE Pricebook2Id = '${pricebook2Id}'\n AND Product2.ProductCode IN (${soqlList})\n`.trim();\n\nreturn [{ soql, codes }];\n"
},
"typeVersion": 2
},
{
"id": "021cd4c3-82b4-4589-a523-bea9bd18ae11",
"name": "Interroger PricebookEntries",
"type": "n8n-nodes-base.salesforce",
"position": [
-64,
-144
],
"parameters": {
"query": "={{ $json.soql }}",
"resource": "search"
},
"credentials": {
"salesforceOAuth2Api": {
"id": "LDbxyOy7NMH6fin9",
"name": "Salesforce account 2"
}
},
"typeVersion": 1
},
{
"id": "cafe3e46-af7a-44eb-87b2-9fb34f7cb96b",
"name": "Code en JavaScript",
"type": "n8n-nodes-base.code",
"position": [
240,
-144
],
"parameters": {
"jsCode": "// Input assumptions (no Set node):\n// - This Function runs after your SOQL HTTP node.\n// - The current item ($json) contains:\n// • records: array of PricebookEntry rows (from SOQL)\n// • opportunityId: the target Opportunity Id\n// • lines: your source line array (with name, quantity, unit_price, discount_amount, ...)\n\nconst oppId = $('Create an opportunity').first().json.id;\nif (!oppId) {\n throw new Error(\"Missing opportunityId in input.\");\n}\n\nconst lines = Array.isArray($('Message a model').first().json.message.content.products) ? $('Message a model').first().json.message.content.products : [];\nif (lines.length === 0) {\n throw new Error(\"Missing or empty lines array in input.\");\n}\n\nconst pbeRecords = Array.isArray($input.all()) ? $input.all() : [];\n\nif (pbeRecords.length === 0) {\n throw new Error(\"Missing SOQL result records in input (records[]).\");\n}\n\n\n\n// Build ProductCode → PricebookEntryId map\nconst pbeByCode = new Map();\nfor (const r of pbeRecords) {\n const code = r?.json.Product2?.ProductCode;\n const id = r?.json.Id;\n if (code && id) pbeByCode.set(String(code).trim(), id);\n}\nconsole.log(pbeByCode)\n// Build OpportunityLineItem payloads\nconst records = [];\nfor (const line of lines) {\n const productCode = String(line.name || \"\").trim();\n const pbeId = pbeByCode.get(productCode);\n\n if (!pbeId) {\n throw new Error(`No PricebookEntry found for ProductCode '${productCode}'.`);\n }\n\n const qty = Number(line.quantity ?? 0) || 0;\n const unitPrice = Number(line.unit_price ?? 0) || 0;\n\n // Convert total discount to per-unit Discount field\n const totalDiscount = Number(line.discount_amount ?? 0) || 0;\n const perUnitDiscount = qty > 0 ? totalDiscount / qty : 0;\n\n records.push({\n attributes: { type: \"OpportunityLineItem\" },\n OpportunityId: oppId,\n PricebookEntryId: pbeId,\n Quantity: qty,\n UnitPrice: unitPrice,\n });\n}\n\nreturn [{ body: { allOrNone: false, records } }];\n"
},
"typeVersion": 2
},
{
"id": "22eb1f99-b562-4160-bf89-6b0dd890a308",
"name": "Créer des éléments de ligne d'opportunité Line",
"type": "n8n-nodes-base.httpRequest",
"position": [
576,
-144
],
"parameters": {
"url": "=https://mydomain.salesforce.com/services/data/v65.0/composite/sobjects",
"method": "POST",
"options": {},
"jsonBody": "={{ $json.body }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "salesforceOAuth2Api"
},
"credentials": {
"salesforceOAuth2Api": {
"id": "LDbxyOy7NMH6fin9",
"name": "Salesforce account 2"
}
},
"typeVersion": 4.2
},
{
"id": "39ef7a5e-6825-4583-b9d2-dbc534bb7c5c",
"name": "Note adhésive1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2048,
-448
],
"parameters": {
"width": 256,
"height": 272,
"content": "## 1) Google Drive Trigger\n- Op: fileCreated in folder (ID)\n- Poll: every minute\n- Creds: Google Drive OAuth2\n- Note: Ensure access/Shared Drive rights"
},
"typeVersion": 1
},
{
"id": "99e69d7e-2ad1-40ef-b476-5ae869e5f927",
"name": "Note adhésive2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1776,
-448
],
"parameters": {
"height": 272,
"content": "## 2) Download from Drive\n- Op: download by {{$json.id}}\n- Binary key: data (default)\n- Creds: Google Drive OAuth2"
},
"typeVersion": 1
},
{
"id": "0830b83e-19f5-489e-a9bd-1eb3e1f7e50b",
"name": "Note adhésive3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1520,
-448
],
"parameters": {
"width": 224,
"height": 272,
"content": "## 3) Extract from File\n- Op: pdf (enable OCR for scans)\n- Output: {{$json.text}}"
},
"typeVersion": 1
},
{
"id": "edb057fe-1446-4aec-929b-7022c9704ee1",
"name": "Note adhésive4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1280,
-512
],
"parameters": {
"width": 256,
"height": 352,
"content": "## 4) Message a Model (JSON)\n- Node: @n8n/n8n-nodes-langchain.openAi\n- Model: gpt-4.1 (or gpt-4.1-mini)\n- Prompt: strict schema; **return only JSON array**\n- jsonOutput: true\n- Output path (first item): $('Message a model').first().json.message.content"
},
"typeVersion": 1
},
{
"id": "4f193f7a-2f69-4eb3-9ee6-e15bd50236d3",
"name": "Note adhésive5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1008,
-512
],
"parameters": {
"height": 352,
"content": "## 5) Salesforce: Upsert Account (Buyer)\n- Node: salesforce (account upsert)\n- External Id: tax_id__c\n- externalIdValue: ={{ $json.message.content.buyer.tax_id }}\n- name: ={{ $json.message.content.buyer.name }}"
},
"typeVersion": 1
},
{
"id": "12811762-358e-4cb7-aec6-92f9f93d47ae",
"name": "Note adhésive6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-752,
-576
],
"parameters": {
"width": 304,
"height": 416,
"content": "## 6) Salesforce: Create Opportunity\n- Node: salesforce (resource: opportunity)\n- name: ={{ $('Message a model').item.json.message.content.invoice.code }}\n- closeDate: ={{ $('Message a model').item.json.message.content.invoice.issue_date }}\n- stageName: Closed Won\n- amount: ={{ $('Message a model').item.json.message.content.summary.grand_total }}\n- accountId: ={{ $json.id }} // from Upsert Account"
},
"typeVersion": 1
},
{
"id": "0c2e625e-0062-480f-b394-45ea687db779",
"name": "Note adhésive7",
"type": "n8n-nodes-base.stickyNote",
"position": [
-432,
-384
],
"parameters": {
"width": 288,
"height": 224,
"content": "## 7) Code: Build SOQL (PricebookEntry)\n- Node: Code (JS)\n- Purpose: collect ProductCodes from LLM products → build SOQL for PricebookEntry by Pricebook2Id\n- Output: { soql, codes }"
},
"typeVersion": 1
},
{
"id": "9b390d82-6233-4d6f-b606-64efadddd654",
"name": "Note adhésive8",
"type": "n8n-nodes-base.stickyNote",
"position": [
-128,
-352
],
"parameters": {
"height": 192,
"content": "## 8) Salesforce: Query PricebookEntries\n- Node: salesforce (search)\n- query: ={{ $json.soql }}"
},
"typeVersion": 1
},
{
"id": "b4ca0bf1-925c-40c6-ba35-208e70a63be1",
"name": "Note adhésive9",
"type": "n8n-nodes-base.stickyNote",
"position": [
128,
-528
],
"parameters": {
"width": 320,
"height": 368,
"content": "## 9) Code: Build OLI Payloads\n- Node: Code (JS)\n- Inputs: \n - OpportunityId: ={{ $('Create an opportunity').first().json.id }}\n - Lines: ={{ $('Message a model').first().json.message.content.products }}\n - PBE rows: from previous node items\n- Output: { body: { allOrNone:false, records:[{ OpportunityLineItem... }] } }"
},
"typeVersion": 1
},
{
"id": "0d8dcf4b-77d7-439f-b345-f0fcec555b01",
"name": "Note adhésive10",
"type": "n8n-nodes-base.stickyNote",
"position": [
480,
-464
],
"parameters": {
"width": 272,
"height": 272,
"content": "## 10) HTTP Request: Create Opportunity Line Items\n- Method: POST\n- URL: https://<your-instance>.my.salesforce.com/services/data/v65.0/composite/sobjects\n- Auth: salesforceOAuth2Api\n- Body (JSON): ={{ $json.body }}"
},
"typeVersion": 1
},
{
"id": "73d319ea-e10d-4f10-8132-63976a3cfacf",
"name": "Note adhésive11",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1488,
176
],
"parameters": {
"height": 272,
"content": "## 11) Upload to OneDrive\n- Op: upload\n- Name: ={{ $json.name }}\n- Parent Folder ID: (set)\n- Binary: from step 2\n- Creds: OneDrive OAuth2"
},
"typeVersion": 1
}
],
"pinData": {},
"connections": {
"70336cbd-460f-4396-964c-196baf054d8f": {
"main": [
[
{
"node": "021cd4c3-82b4-4589-a523-bea9bd18ae11",
"type": "main",
"index": 0
}
]
]
},
"ce439fd0-ffcf-4891-a861-61fa62e57a38": {
"main": [
[
{
"node": "9f06013f-ff72-4f08-b72e-4b42931f393f",
"type": "main",
"index": 0
}
]
]
},
"e7665458-f2f8-4ad1-b318-1fbe4e3ff464": {
"main": [
[
{
"node": "ce439fd0-ffcf-4891-a861-61fa62e57a38",
"type": "main",
"index": 0
}
]
]
},
"cafe3e46-af7a-44eb-87b2-9fb34f7cb96b": {
"main": [
[
{
"node": "22eb1f99-b562-4160-bf89-6b0dd890a308",
"type": "main",
"index": 0
}
]
]
},
"1a593bd5-922f-4f0d-9ddb-0220ed3be3cb": {
"main": [
[
{
"node": "053cdfcf-65f6-4ec6-90fa-090a08da31c8",
"type": "main",
"index": 0
}
]
]
},
"80f186d6-499e-4da2-a49e-a503de546be4": {
"main": [
[
{
"node": "70336cbd-460f-4396-964c-196baf054d8f",
"type": "main",
"index": 0
}
]
]
},
"021cd4c3-82b4-4589-a523-bea9bd18ae11": {
"main": [
[
{
"node": "cafe3e46-af7a-44eb-87b2-9fb34f7cb96b",
"type": "main",
"index": 0
}
]
]
},
"053cdfcf-65f6-4ec6-90fa-090a08da31c8": {
"main": [
[
{
"node": "e7665458-f2f8-4ad1-b318-1fbe4e3ff464",
"type": "main",
"index": 0
},
{
"node": "157f5cdc-de47-4f90-96ea-202636ac5016",
"type": "main",
"index": 0
}
]
]
},
"9f06013f-ff72-4f08-b72e-4b42931f393f": {
"main": [
[
{
"node": "80f186d6-499e-4da2-a49e-a503de546be4",
"type": "main",
"index": 0
}
]
]
},
"22eb1f99-b562-4160-bf89-6b0dd890a308": {
"main": [
[]
]
}
}
}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é ?
Avancé - Traitement des factures, Résumé IA
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.
Workflows recommandés
Le Nguyen
@leeseiferSalesforce Architect with 10+ years of experience in CRM, integrations, and automation. Skilled in Apex, LWC, REST APIs, and full-stack dev (JavaScript, .NET). I build secure, scalable workflows in n8n—connecting Salesforce, Stripe, and more. Passionate about lead scoring, data sync, and secure field masking. Certified Application Architect with deep expertise in platform, integration, and data architecture.
Partager ce workflow