Back to Templates
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.
fileCreatedgoogle drive folder ideveryMinutegoogleDriveOAuth2Api{ id, name, ... } for the new file.download={{ $json.id }}googleDriveOAuth2Apidata) and original metadata.pdf{{ $json.text }}.@n8n/n8n-nodes-langchain.openAigpt-4.1 (or gpt-4.1-mini){{ $json.text }})jsonOutput: trueopenAiApi$.message.content → the parsed JSON (ensure it’s an array).accountupserttax_id__c={{ $json.message.content.buyer.tax_id }}={{ $json.message.content.buyer.name }}salesforceOAuth2ApiId) for downstream Opportunity.opportunity={{ $('Message a model').item.json.message.content.invoice.code }}={{ $('Message a model').item.json.message.content.invoice.issue_date }}Closed Won={{ $('Message a model').item.json.message.content.summary.grand_total }}={{ $json.id }} (from Upsert Account output)salesforceOAuth2ApiId for OLI creation.Pricebook2Id.pricebook2Id (hardcoded in script): e.g., 01sxxxxxxxxxxxxxxx$('Message a model').first().json.message.content.products{ soql, codes }PricebookEntry.Id for each Product2.ProductCode.search={{ $json.soql }}salesforceOAuth2ApiId, Product2.ProductCode (used for mapping).Id ➜ build OpportunityLineItem payloads.={{ $('Create an opportunity').first().json.id }}={{ $('Message a model').first().json.message.content.products }}{ body: { allOrNone:false, records:[{ OpportunityLineItem... }] } }POSThttps://<your-instance>.my.salesforce.com/services/data/v65.0/composite/sobjectssalesforceOAuth2Api (predefined credential)={{ $json.body }}upload={{ $json.name }}onedrive folder idtrue (from the Download node)microsoftOneDriveOAuth2Apiname must equal your Product2.ProductCode in SF.jsonOutput enabled.parsing.warnings to gate Salesforce writes.