{ "kind": "open-flow-workflow", "version": 1, "name": "LeadPilot Cold Email Drafts", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_1b80efa7f4a14b42": { "name": "Test a lead batch", "kind": "start", "inputDefs": [ { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "request" } ] }, "n_56c9aa3c30c78b18": { "name": "Receive a lead batch", "kind": "webhook", "bodyFields": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": true, "handle": "leads" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "name" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "email" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "title" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "company" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "industry" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "company_description" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "linkedin_url" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "notes" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "sender_name" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "sender_company" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "value_prop" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "calendar_link" } ], "method": "POST", "options": { "responseBody": { "kind": "workflow-outputs", "timeoutMs": 60000 } } }, "n_69d96bd472ce7533": { "inputs": { "request": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_1b80efa7f4a14b42", "output": "request" }, { "kind": "node", "nodeId": "n_56c9aa3c30c78b18", "output": "body" } ] } }, "name": "Validate every lead and sender", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "request" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "leads" } ], "name": "Validate every lead and sender", "moduleId": "n_bde9866a7196c378", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_c4d0b1eab592c223": { "inputs": { "leads": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_69d96bd472ce7533", "output": "leads" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c4d0b1eab592c223", "output": "state" } ] }, "response": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_4946158b1b32f14c", "output": "output" } ] } }, "name": "Collect validated lead drafts", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "leads" }, { "jsonSchema": { "type": "object", "properties": { "index": { "type": "integer", "minimum": 0 }, "drafts": { "type": "array", "items": { "type": "object", "additionalProperties": true } } }, "required": [ "index", "drafts" ], "additionalProperties": false }, "nullable": true, "handle": "previousState" }, { "jsonSchema": { "type": "object" }, "nullable": true, "handle": "response" } ], "outputs": [ { "jsonSchema": { "type": "object", "properties": { "index": { "type": "integer", "minimum": 0 }, "drafts": { "type": "array", "items": { "type": "object", "additionalProperties": true } } }, "required": [ "index", "drafts" ], "additionalProperties": false }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "prompt" }, { "jsonSchema": { "type": "array", "items": { "type": "array", "items": { "type": "string" } } }, "nullable": false, "handle": "values" } ], "name": "Collect validated lead drafts", "moduleId": "n_52ddde3241871007", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_7e4f7a6be3f0149a": { "inputs": {}, "name": "Another lead needs drafts", "kind": "condition", "matchMode": "first", "cases": [ { "output": "generate", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_c4d0b1eab592c223", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_4946158b1b32f14c": { "inputs": { "prompt": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c4d0b1eab592c223", "output": "prompt" } ] } }, "name": "Write one cold email and follow-up", "retry": { "maxRetries": 2, "intervalMs": 2000 }, "kind": "task", "task": { "inputs": [ { "jsonSchema": { "items": { "type": "object" }, "type": "array" }, "nullable": true, "handle": "messages", "value": null }, { "jsonSchema": { "items": { "type": "object" }, "minItems": 1, "type": "array" }, "nullable": false, "handle": "template", "value": [ { "role": "system", "content": "You are an elite cold email copywriter. Write a personalized cold email.\n\nRules:\n- Subject: 3-7 words, personalized, zero spam triggers\n- Opening: Reference their company or role specifically\n- Body: 50-80 words MAX. One pain point + one value prop.\n- CTA: Soft question or calendar link\n- Include 1 follow-up email (3 days later, 30-50 words)\n\nJSON: { \"subjectLine\": \"...\", \"emailBody\": \"full email\", \"followUp\": { \"subject\": \"Re: [subject]\", \"body\": \"follow-up text\" } }" }, { "role": "user", "content": "{{prompt}}" } ] }, { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "model", "value": { "model": "gpt-4o-mini", "temperature": 0.7, "max_tokens": 800 } }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "prompt" } ], "outputs": [ { "description": "Generated response.", "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "output" } ], "name": "Write one cold email and follow-up", "executor": { "kind": "llm", "mode": "json" } } }, "n_71c6ecb9676d0cbc": { "inputs": { "spreadsheetId": { "kind": "value", "value": "YOUR_SPREADSHEET_ID" }, "range": { "kind": "value", "value": "'Lite Pipeline'!A:I" }, "values": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c4d0b1eab592c223", "output": "values" } ] }, "valueInputOption": { "kind": "value", "value": "RAW" }, "insertDataOption": { "kind": "value", "value": "INSERT_ROWS" } }, "name": "Save the complete batch for review", "kind": "task", "task": { "inputs": [ { "description": "The spreadsheet ID.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The spreadsheet ID." }, "nullable": true, "handle": "spreadsheetId", "value": null }, { "description": "The A1 notation of the range to append values after.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to append values after." }, "nullable": false, "handle": "range" }, { "description": "The data to append.", "jsonSchema": { "type": "array", "items": { "type": "array", "items": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "number", "description": "A union variant." }, { "type": "boolean", "description": "A union variant." }, { "const": null, "description": "A union variant." } ], "description": "The cell value schema." }, "description": "An array item." }, "description": "The data to append." }, "nullable": false, "handle": "values" }, { "description": "The major dimension of the values array.", "jsonSchema": { "type": "string", "enum": [ "ROWS", "COLUMNS" ], "description": "The major dimension of the values array." }, "nullable": true, "handle": "majorDimension", "value": null }, { "description": "How the data should be inserted.", "jsonSchema": { "type": "string", "enum": [ "OVERWRITE", "INSERT_ROWS" ], "description": "How the data should be inserted." }, "nullable": true, "handle": "insertDataOption", "value": null }, { "description": "How the input data should be interpreted.", "jsonSchema": { "type": "string", "enum": [ "RAW", "USER_ENTERED" ], "description": "How the input data should be interpreted." }, "nullable": true, "handle": "valueInputOption", "value": null }, { "description": "Whether to include the appended values in the response.", "jsonSchema": { "type": "boolean", "description": "Whether to include the appended values in the response." }, "nullable": true, "handle": "includeValuesInResponse", "value": null }, { "description": "How values in the response should be rendered.", "jsonSchema": { "type": "string", "enum": [ "FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA" ], "description": "How values in the response should be rendered." }, "nullable": true, "handle": "responseValueRenderOption", "value": null }, { "description": "How dates in the response should be rendered.", "jsonSchema": { "type": "string", "enum": [ "SERIAL_NUMBER", "FORMATTED_STRING" ], "description": "How dates in the response should be rendered." }, "nullable": true, "handle": "responseDateTimeRenderOption", "value": null } ], "outputs": [ { "description": "The unique ID of the spreadsheet.", "jsonSchema": { "type": "string", "description": "The unique ID of the spreadsheet." }, "nullable": false, "handle": "spreadsheetId" }, { "description": "The A1 notation of the range that was updated.", "jsonSchema": { "type": "string", "description": "The A1 notation of the range that was updated." }, "nullable": false, "handle": "updatedRange" }, { "description": "The number of rows that were updated.", "jsonSchema": { "type": "integer", "description": "The number of rows that were updated." }, "nullable": false, "handle": "updatedRows" }, { "description": "The number of columns that were updated.", "jsonSchema": { "type": "integer", "description": "The number of columns that were updated." }, "nullable": false, "handle": "updatedColumns" }, { "description": "The number of cells that were updated.", "jsonSchema": { "type": "integer", "description": "The number of cells that were updated." }, "nullable": false, "handle": "updatedCells" }, { "description": "The values of the cells after the update.", "jsonSchema": { "type": "object", "properties": { "range": { "type": "string", "description": "The A1 notation of the range." }, "majorDimension": { "type": "string", "description": "The major dimension of the values (ROWS or COLUMNS)." }, "values": { "type": "array", "items": { "type": "array", "items": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "number", "description": "A union variant." }, { "type": "boolean", "description": "A union variant." }, { "const": null, "description": "A union variant." } ], "description": "The cell value schema." }, "description": "An array item." }, "description": "The data values in the range." } }, "required": [ "range", "majorDimension", "values" ], "additionalProperties": false, "description": "The values of the cells after the update." }, "nullable": true, "handle": "updatedData" }, { "description": "The A1 notation of the table range that data was appended to.", "jsonSchema": { "type": "string", "description": "The A1 notation of the table range that data was appended to." }, "nullable": false, "handle": "tableRange" } ], "name": "Save the complete batch for review", "executor": { "kind": "connector", "action": "googlesheets.spreadsheets_values_append" } } }, "n_37bb5492aa9bd175": { "inputs": { "drafts": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c4d0b1eab592c223", "output": "state", "field": "drafts" } ] }, "updatedRows": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_71c6ecb9676d0cbc", "output": "updatedRows" } ] }, "updatedRange": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_71c6ecb9676d0cbc", "output": "updatedRange" } ] } }, "name": "Confirm all lead rows were saved", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "drafts" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "updatedRows" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "updatedRange" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "success" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "lead" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "company" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "subjectLine" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "drafts" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "updatedRange" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "message" } ], "name": "Confirm all lead rows were saved", "moduleId": "n_684070830eb14d49", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_3f99fdbea9af56ba": { "name": "Return saved lead drafts", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "success" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "lead" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "company" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "subjectLine" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "drafts" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "updatedRange" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "message" } ], "outputMappings": { "success": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_37bb5492aa9bd175", "output": "success" } ] }, "lead": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_37bb5492aa9bd175", "output": "lead" } ] }, "company": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_37bb5492aa9bd175", "output": "company" } ] }, "subjectLine": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_37bb5492aa9bd175", "output": "subjectLine" } ] }, "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_37bb5492aa9bd175", "output": "count" } ] }, "drafts": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_37bb5492aa9bd175", "output": "drafts" } ] }, "updatedRange": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_37bb5492aa9bd175", "output": "updatedRange" } ] }, "message": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_37bb5492aa9bd175", "output": "message" } ] } } } }, "edges": [ { "source": "n_1b80efa7f4a14b42", "target": "n_69d96bd472ce7533" }, { "source": "n_56c9aa3c30c78b18", "target": "n_69d96bd472ce7533" }, { "source": "n_69d96bd472ce7533", "target": "n_c4d0b1eab592c223" }, { "source": "n_c4d0b1eab592c223", "target": "n_7e4f7a6be3f0149a" }, { "source": "n_7e4f7a6be3f0149a", "target": "n_4946158b1b32f14c", "sourceHandle": "generate" }, { "source": "n_4946158b1b32f14c", "target": "n_c4d0b1eab592c223" }, { "source": "n_7e4f7a6be3f0149a", "target": "n_71c6ecb9676d0cbc", "sourceHandle": "otherwise" }, { "source": "n_71c6ecb9676d0cbc", "target": "n_37bb5492aa9bd175" }, { "source": "n_37bb5492aa9bd175", "target": "n_3f99fdbea9af56ba" } ] } }, "modules": { "n_bde9866a7196c378": { "name": "Validate every lead and sender", "imports": [], "source": "export default function prepareLeadPilot({request}) {\n if (!request || typeof request !== 'object' || Array.isArray(request)) throw new Error('Provide a lead object or an object containing leads.');\n const source = request.leads === undefined ? [request] : request.leads;\n if (!Array.isArray(source) || source.length === 0) throw new Error('Provide at least one lead.');\n const text = (value, name, fallback = '') => {\n if (value === undefined || value === null || value === '') return fallback;\n if (typeof value !== 'string') throw new Error(name + ' must be text.');\n return value;\n };\n const senderName=text(request.sender_name,'sender_name','Your Name'),senderCompany=text(request.sender_company,'sender_company','Your Company'),senderValue=text(request.value_prop,'value_prop','We help companies save time with automation.'),calendarLink=text(request.calendar_link,'calendar_link');\n const leads=source.map((lead,index)=>{\n if (!lead || typeof lead !== 'object' || Array.isArray(lead) || typeof lead.name !== 'string' || !lead.name.trim()) throw new Error('Lead '+(index+1)+' requires a name.');\n return {name:lead.name,email:text(lead.email,'email'),title:text(lead.title,'title'),company:text(lead.company,'company'),industry:text(lead.industry,'industry'),companyDescription:text(lead.company_description,'company_description'),linkedinUrl:text(lead.linkedin_url,'linkedin_url'),notes:text(lead.notes,'notes'),senderName,senderCompany,senderValue,calendarLink};\n });\n return {leads};\n}\n" }, "n_52ddde3241871007": { "name": "Collect validated lead drafts", "imports": [], "source": "export function validateLeadPilotEmail(value) {\n if(typeof value==='string') { try { value=JSON.parse(value); } catch { throw new Error('The model response is not JSON.'); } }\n if(!value || typeof value!=='object' || Array.isArray(value)) throw new Error('The model response must be an email object.');\n const required=(v,name)=>{if(typeof v!=='string'||!v.trim())throw new Error('Generated '+name+' is empty or missing.');return v;};\n return {subjectLine:required(value.subjectLine,'subjectLine'),emailBody:required(value.emailBody,'emailBody'),followUp:{subject:required(value.followUp?.subject,'followUp.subject'),body:required(value.followUp?.body,'followUp.body')}};\n}\nexport function leadPilotPrompt(lead) {\n return 'Write a cold email:\\n\\nRecipient: '+lead.name+'\\nTitle: '+lead.title+'\\nCompany: '+lead.company+'\\nIndustry: '+lead.industry+'\\nAbout Company: '+lead.companyDescription+'\\nNotes: '+lead.notes+'\\n\\nSender: '+lead.senderName+' at '+lead.senderCompany+'\\nValue Prop: '+lead.senderValue+'\\nCalendar: '+lead.calendarLink;\n}\nexport default function collectLeadPilot({leads,previousState,response}) {\n let state=previousState??{index:0,drafts:[]};\n if(!Number.isSafeInteger(state.index)||state.index<0||state.index>leads.length||state.drafts.length!==state.index)throw new Error('Invalid lead generation progress.');\n if(previousState && state.index[d.name,d.email,d.title,d.company,d.subjectLine,d.emailBody,d.followUp.body,d.generatedAt,'No'])};\n}\n" }, "n_684070830eb14d49": { "name": "Confirm all lead rows were saved", "imports": [], "source": "export default function confirmLeadPilot({drafts,updatedRows,updatedRange}) {\n if(updatedRows!==drafts.length||typeof updatedRange!=='string'||!updatedRange.trim())throw new Error('The complete lead batch was not confirmed saved. Inspect the sheet before repeating this request.');\n const first=drafts[0];\n return {success:true,lead:first.name,company:first.company,subjectLine:first.subjectLine,count:drafts.length,drafts,updatedRange:updatedRange,message:'Emails generated. Check Google Sheets to review and send.'};\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }