This workflow corresponds to n8n.io template #10029 — we link there as the canonical source.
This workflow follows the Google Drive → Google Drive Trigger recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"meta": {
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "c87ed7b7-b77d-4236-999e-afefe9064033",
"name": "Sticky Note",
"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 \u2192 OCR PDF \u2192 extract invoices (JSON) via LLM \u2192 mirror file to OneDrive \u2192 upsert Buyer to Salesforce \u2192 create Opportunity \u2192 add Opportunity Line Items from Pricebook.\n\n\n## Flow\n 1)Drive Trigger \u2192 2) Download \u2192 3) Extract \u2192 4) \u2192 5) Upsert Account \u2192 6) Create Opportunity \u2192 7) Build SOQL \u2192 8) Query PBE \u2192 9) Build OLI \u2192 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 \u2192 enable OCR in step 3\n- Non-JSON \u2192 enforce jsonOutput:true + strict prompt\n- Missing PBE \u2192 verify ProductCode matches Salesforce & Pricebook2Id\n- Auth errors \u2192 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": "Extract from File",
"type": "n8n-nodes-base.extractFromFile",
"position": [
-1456,
-144
],
"parameters": {
"options": {},
"operation": "pdf"
},
"typeVersion": 1
},
{
"id": "1a593bd5-922f-4f0d-9ddb-0220ed3be3cb",
"name": "Google Drive Trigger",
"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": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "ce439fd0-ffcf-4891-a861-61fa62e57a38",
"name": "Message a model",
"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, \u20ab, $, \u20ac).\n - Remove thousands separators (., , or space).\n - Interpret decimals by locale (e.g., \u201c1,234.56\u201d vs \u201c1.234,56\u201d).\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 \u201cdd Month yyyy\u201d.\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) \u2248 summary.subtotal_excl_vat (\u00b11 minor unit)\n - summary.vat_amount \u2248 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- \u201cTAX INVOICE\u201d, \u201cINVOICE\u201d, \u201cVAT INVOICE\u201d, or similar header; OR\n- A cluster containing both \u201cSerial/Symbol:\u201d (or \u201cSeries:\u201d) and \u201cNo./Number:\u201d within ~10 lines.\n\nSecondary boundaries (helpful, not required):\n- \u201cTax Authority Code / QR / Validation Code\u201d near header\n- \u201cAmount in words:\u201d or \u201cTotal amount payable:\u201d near the end\n- Software footers (e.g., \u201cIssued by \u2026 software\u201d)\n\nHeuristic:\n- Start a new block when a header reappears or when \u201cSeries/Symbol:\u201d and \u201cNo./Number:\u201d occur after a prior \u201cTotal amount payable\u201d.\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\u2019t 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": {
"name": "<your credential>"
}
},
"typeVersion": 1.8
},
{
"id": "053cdfcf-65f6-4ec6-90fa-090a08da31c8",
"name": "Download File From Google",
"type": "n8n-nodes-base.googleDrive",
"position": [
-1712,
-144
],
"parameters": {
"fileId": {
"__rl": true,
"mode": "id",
"value": "={{ $json.id }}"
},
"options": {},
"operation": "download"
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3
},
{
"id": "157f5cdc-de47-4f90-96ea-202636ac5016",
"name": "Update File to One Drive",
"type": "n8n-nodes-base.microsoftOneDrive",
"position": [
-1424,
32
],
"parameters": {
"fileName": "={{ $json.name }}",
"parentId": "folder_id_onedrive",
"binaryData": true
},
"credentials": {
"microsoftOneDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "80f186d6-499e-4da2-a49e-a503de546be4",
"name": "Create an opportunity",
"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": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "9f06013f-ff72-4f08-b72e-4b42931f393f",
"name": "Create or update an account",
"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": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "70336cbd-460f-4396-964c-196baf054d8f",
"name": "Build 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": "Query PricebookEntries",
"type": "n8n-nodes-base.salesforce",
"position": [
-64,
-144
],
"parameters": {
"query": "={{ $json.soql }}",
"resource": "search"
},
"credentials": {
"salesforceOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "cafe3e46-af7a-44eb-87b2-9fb34f7cb96b",
"name": "Code in 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// \u2022 records: array of PricebookEntry rows (from SOQL)\n// \u2022 opportunityId: the target Opportunity Id\n// \u2022 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 \u2192 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": "Create Opportunity Line Items",
"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": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "39ef7a5e-6825-4583-b9d2-dbc534bb7c5c",
"name": "Sticky Note1",
"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": "Sticky Note2",
"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": "Sticky Note3",
"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": "Sticky Note4",
"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": "Sticky Note5",
"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": "Sticky Note6",
"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": "Sticky Note7",
"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 \u2192 build SOQL for PricebookEntry by Pricebook2Id\n- Output: { soql, codes }"
},
"typeVersion": 1
},
{
"id": "9b390d82-6233-4d6f-b606-64efadddd654",
"name": "Sticky Note8",
"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": "Sticky Note9",
"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": "Sticky Note10",
"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": "Sticky Note11",
"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
}
],
"connections": {
"Build SOQL": {
"main": [
[
{
"node": "Query PricebookEntries",
"type": "main",
"index": 0
}
]
]
},
"Message a model": {
"main": [
[
{
"node": "Create or update an account",
"type": "main",
"index": 0
}
]
]
},
"Extract from File": {
"main": [
[
{
"node": "Message a model",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript": {
"main": [
[
{
"node": "Create Opportunity Line Items",
"type": "main",
"index": 0
}
]
]
},
"Google Drive Trigger": {
"main": [
[
{
"node": "Download File From Google",
"type": "main",
"index": 0
}
]
]
},
"Create an opportunity": {
"main": [
[
{
"node": "Build SOQL",
"type": "main",
"index": 0
}
]
]
},
"Query PricebookEntries": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
]
]
},
"Download File From Google": {
"main": [
[
{
"node": "Extract from File",
"type": "main",
"index": 0
},
{
"node": "Update File to One Drive",
"type": "main",
"index": 0
}
]
]
},
"Create or update an account": {
"main": [
[
{
"node": "Create an opportunity",
"type": "main",
"index": 0
}
]
]
},
"Create Opportunity Line Items": {
"main": [
[]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
googleDriveOAuth2ApimicrosoftOneDriveOAuth2ApiopenAiApisalesforceOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
End-to-end pipeline: Watch Drive ➜ Download PDF ➜ OCR text ➜ AI normalize to JSON ➜ Upsert Buyer (Account) ➜ Create Opportunity ➜ Map Products ➜ Create OLI via Composite API ➜ Archive to OneDrive. Purpose: Fire when a new file appears in a specific Google Drive folder. Key…
Source: https://n8n.io/workflows/10029/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
The Problem That it Solves
Content creators, YouTubers, and social media managers who want to repurpose long form videos into short clips without doing it manually. Works on self hosted n8n instances.
This workflow automatically turns any audio file uploaded to Google Drive into a complete podcast episode. It handles transcription, content generation, blog drafting, social copy creation, thumbnail
This template is ideal for photographers, graphic designers, and creative professionals who manage large volumes of visual assets. It is also perfect for Digital Asset Managers looking for a customiza
This workflow automatically analyzes any newly uploaded APK file and produces a clean, professional PDF security report. When an APK appears in Google Drive, the workflow downloads it, sends it to Mob