{ "kind": "open-flow-workflow", "version": 1, "name": "Create Odoo Opportunities from Sales Emails", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_e1a071df14493d92": { "name": "New sales emails each hour", "kind": "poll", "config": { "labelNamesOrIds": { "kind": "value", "value": [ "YOUR_SALES_LABEL_ID" ] }, "readStatus": { "kind": "value", "value": "All" }, "search": { "kind": "value", "value": "" }, "sender": { "kind": "value", "value": "" }, "includeDrafts": { "kind": "value", "value": false }, "includeSpamAndTrash": { "kind": "value", "value": false }, "maxMessagesPerPoll": { "kind": "value", "value": 25 } }, "definition": { "configInputs": [ { "description": "Only trigger on messages carrying all of these labels, referenced by name or ID.", "jsonSchema": { "type": "array", "items": { "type": "string", "minLength": 1 } }, "nullable": false, "handle": "labelNamesOrIds", "value": [] }, { "jsonSchema": { "type": "string", "enum": [ "Read", "Unread", "All" ] }, "nullable": false, "handle": "readStatus", "value": "All" }, { "description": "Gmail search-syntax query the message must match.", "jsonSchema": { "type": "string" }, "nullable": false, "handle": "search", "value": "" }, { "description": "Only trigger on messages whose sender matches this address or name.", "jsonSchema": { "type": "string" }, "nullable": false, "handle": "sender", "value": "" }, { "collapsed": true, "group": "Advanced settings" }, { "description": "Also trigger on draft messages.", "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "includeDrafts", "value": false }, { "description": "Also trigger on messages in SPAM and TRASH.", "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "includeSpamAndTrash", "value": false }, { "jsonSchema": { "type": "integer", "minimum": 1, "maximum": 100 }, "nullable": false, "handle": "maxMessagesPerPoll", "value": 25 } ], "definitionVersion": 2, "description": "Polls the Gmail mailbox and triggers when a new message is received.", "displayName": "New Message Received", "key": "gmail.on_message_received", "name": "on_message_received", "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "historyId": { "type": [ "string", "null" ] }, "labelIds": { "type": "array", "items": { "type": "string" } }, "messageId": { "type": "string" }, "messageTimestamp": { "type": "string" }, "sender": { "type": "string" }, "subject": { "type": "string" }, "threadId": { "type": "string" }, "to": { "type": "string" } }, "additionalProperties": false, "required": [ "messageId", "threadId", "historyId", "labelIds", "subject", "sender", "to", "messageTimestamp" ] } }, "nullable": false, "handle": "events" } ], "provider": "gmail", "type": "poll" }, "pollTimes": [ { "type": "every", "unit": "hour", "value": 1 } ] }, "n_aee6e0b473cc764c": { "inputs": { "events": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e1a071df14493d92", "output": "events" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_aee6e0b473cc764c", "output": "state" } ] }, "createdId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2e9534492c85def8", "output": "id" } ] } }, "name": "Choose the next email", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "historyId": { "type": [ "string", "null" ] }, "labelIds": { "type": "array", "items": { "type": "string" } }, "messageId": { "type": "string" }, "messageTimestamp": { "type": "string" }, "sender": { "type": "string" }, "subject": { "type": "string" }, "threadId": { "type": "string" }, "to": { "type": "string" } }, "additionalProperties": false, "required": [ "messageId", "threadId", "historyId", "labelIds", "subject", "sender", "to", "messageTimestamp" ] } }, "nullable": false, "handle": "events" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" }, { "jsonSchema": { "type": "integer" }, "nullable": true, "handle": "createdId" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "messageId" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "name": "Choose the next email", "moduleId": "n_2c4a04b0445c5234", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_807998d7deeac695": { "inputs": {}, "name": "Any emails remaining", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_aee6e0b473cc764c", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_9a3744335397be64": { "inputs": { "messageId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_aee6e0b473cc764c", "output": "messageId" } ] }, "format": { "kind": "value", "value": "full" } }, "name": "Read the sales email", "kind": "task", "task": { "inputs": [ { "description": "Gmail message ID.", "jsonSchema": { "type": "string", "description": "Gmail message ID." }, "nullable": false, "handle": "messageId" }, { "description": "Gmail response format to request.", "jsonSchema": { "type": "string", "enum": [ "minimal", "full", "raw", "metadata" ], "description": "Gmail response format to request." }, "nullable": true, "handle": "format", "value": null } ], "outputs": [ { "description": "The message history ID.", "jsonSchema": { "type": "string", "description": "The message history ID." }, "nullable": true, "handle": "historyId" }, { "description": "The internal timestamp in milliseconds.", "jsonSchema": { "type": "string", "description": "The internal timestamp in milliseconds." }, "nullable": true, "handle": "internalDate" }, { "description": "The estimated message size in bytes.", "jsonSchema": { "type": "integer", "description": "The estimated message size in bytes." }, "nullable": true, "handle": "sizeEstimate" }, { "description": "The message snippet.", "jsonSchema": { "type": "string", "description": "The message snippet." }, "nullable": true, "handle": "snippet" }, { "description": "Gmail message ID.", "jsonSchema": { "type": "string", "description": "Gmail message ID." }, "nullable": false, "handle": "messageId" }, { "description": "Gmail thread ID.", "jsonSchema": { "type": "string", "description": "Gmail thread ID." }, "nullable": false, "handle": "threadId" }, { "description": "Label IDs applied to the message.", "jsonSchema": { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "Label IDs applied to the message." }, "nullable": false, "handle": "labelIds" }, { "description": "Normalized message subject line.", "jsonSchema": { "type": "string", "description": "Normalized message subject line." }, "nullable": false, "handle": "subject" }, { "description": "Normalized sender value.", "jsonSchema": { "type": "string", "description": "Normalized sender value." }, "nullable": false, "handle": "sender" }, { "description": "Normalized To header value.", "jsonSchema": { "type": "string", "description": "Normalized To header value." }, "nullable": false, "handle": "to" }, { "description": "Short preview of the message subject and body.", "jsonSchema": { "type": "object", "properties": { "subject": { "type": "string", "description": "Preview subject extracted from the message." }, "body": { "type": "string", "description": "Preview body text extracted from the message." } }, "required": [ "subject", "body" ], "additionalProperties": false, "description": "Short preview of the message subject and body." }, "nullable": false, "handle": "preview" }, { "description": "Raw Gmail payload object, or null when it was not included.", "jsonSchema": { "type": [ "object", "null" ], "additionalProperties": { "description": "A record value." }, "description": "Raw Gmail payload object, or null when it was not included." }, "nullable": true, "handle": "payload" }, { "description": "Best-effort plain-text representation of the message body.", "jsonSchema": { "type": "string", "description": "Best-effort plain-text representation of the message body." }, "nullable": false, "handle": "messageText" }, { "description": "Normalized attachment summaries.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "attachmentId": { "type": [ "string", "null" ], "description": "Attachment ID, or null when Gmail inlines the body." }, "filename": { "type": "string", "description": "Attachment filename." }, "mimeType": { "type": "string", "description": "Attachment MIME type." }, "size": { "type": "integer", "description": "Attachment size in bytes." }, "partId": { "type": [ "string", "null" ], "description": "Gmail MIME part ID, when supplied." }, "contentId": { "type": [ "string", "null" ], "description": "Bare Content-ID, when supplied." }, "disposition": { "type": [ "string", "null" ], "enum": [ "inline", "attachment", null ], "description": "Content-Disposition, when supplied." } }, "required": [ "attachmentId", "filename", "mimeType", "size", "partId", "contentId", "disposition" ], "additionalProperties": false, "description": "Schema for gmailAttachmentSchema." }, "description": "Normalized attachment summaries." }, "nullable": false, "handle": "attachmentList" }, { "description": "Message timestamp returned by Gmail.", "jsonSchema": { "type": "string", "description": "Message timestamp returned by Gmail." }, "nullable": false, "handle": "messageTimestamp" }, { "description": "Raw RFC 2822 message content when Gmail returns it.", "jsonSchema": { "type": "string", "description": "Raw RFC 2822 message content when Gmail returns it." }, "nullable": true, "handle": "raw" } ], "name": "Read the sales email", "executor": { "kind": "connector", "action": "gmail.fetch_message_by_message_id" } } }, "n_1495ee30ae9aee5c": { "inputs": { "expectedId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_aee6e0b473cc764c", "output": "messageId" } ] }, "messageId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_9a3744335397be64", "output": "messageId" } ] }, "sender": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_9a3744335397be64", "output": "sender" } ] }, "subject": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_9a3744335397be64", "output": "subject" } ] }, "messageText": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_9a3744335397be64", "output": "messageText" } ] } }, "name": "Prepare the inquiry", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "expectedId" }, { "jsonSchema": { "type": "string", "description": "Gmail message ID." }, "nullable": false, "handle": "messageId" }, { "jsonSchema": { "type": "string", "description": "Normalized sender value." }, "nullable": false, "handle": "sender" }, { "jsonSchema": { "type": "string", "description": "Normalized message subject line." }, "nullable": false, "handle": "subject" }, { "jsonSchema": { "type": "string", "description": "Best-effort plain-text representation of the message body." }, "nullable": false, "handle": "messageText" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "email" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "subject" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "body" } ], "name": "Prepare the inquiry", "moduleId": "n_3d67673936ea2b9e", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_daa67cfbe4f01309": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_1495ee30ae9aee5c", "output": "body" } ] } }, "name": "Summarize the sales inquiry", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "body" } ], "outputs": [ { "description": "The final result produced by the Agent.", "jsonSchema": { "type": "string" }, "nullable": false, "handle": "output" } ], "name": "Summarize the sales inquiry", "executor": { "kind": "agent", "code": false, "model": "gpt-4o-mini", "prompt": "Write a concise summary of the following sales inquiry. Include available project budget, timeline, industry and a general summary. Do not invent missing facts.\n\n{{body}}", "maxRounds": 1, "tools": [] } } }, "n_441ec3aaa749e556": { "inputs": { "email": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_1495ee30ae9aee5c", "output": "email" } ] }, "subject": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_1495ee30ae9aee5c", "output": "subject" } ] }, "summary": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_daa67cfbe4f01309", "output": "output" } ] } }, "name": "Prepare the opportunity", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "email" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "subject" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "summary" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "values" } ], "name": "Prepare the opportunity", "moduleId": "n_18f4b2189938556e", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_2e9534492c85def8": { "inputs": { "model": { "kind": "value", "value": "crm.lead" }, "values": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_441ec3aaa749e556", "output": "values" } ] } }, "name": "Create the Odoo opportunity", "kind": "task", "task": { "inputs": [ { "description": "The technical model name, such as res.partner or sale.order.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The technical model name, such as res.partner or sale.order." }, "nullable": false, "handle": "model" }, { "description": "The new record values, including relational command arrays.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "The new record values, including relational command arrays." }, "nullable": false, "handle": "values" }, { "description": "Odoo context values such as lang, tz, or allowed_company_ids.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "Odoo context values such as lang, tz, or allowed_company_ids." }, "nullable": true, "handle": "context", "value": null } ], "outputs": [ { "description": "The created record ID.", "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The created record ID." }, "nullable": false, "handle": "id" } ], "name": "Create the Odoo opportunity", "executor": { "kind": "connector", "action": "odoo.create" } } }, "n_d23fb1209e7edfc4": { "name": "Processed email count", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "outputMappings": { "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_aee6e0b473cc764c", "output": "count" } ] } } } }, "edges": [ { "source": "n_e1a071df14493d92", "target": "n_aee6e0b473cc764c" }, { "source": "n_aee6e0b473cc764c", "target": "n_807998d7deeac695" }, { "source": "n_807998d7deeac695", "target": "n_9a3744335397be64", "sourceHandle": "more" }, { "source": "n_9a3744335397be64", "target": "n_1495ee30ae9aee5c" }, { "source": "n_1495ee30ae9aee5c", "target": "n_daa67cfbe4f01309" }, { "source": "n_daa67cfbe4f01309", "target": "n_441ec3aaa749e556" }, { "source": "n_441ec3aaa749e556", "target": "n_2e9534492c85def8" }, { "source": "n_2e9534492c85def8", "target": "n_aee6e0b473cc764c" }, { "source": "n_807998d7deeac695", "target": "n_d23fb1209e7edfc4", "sourceHandle": "otherwise" } ] } }, "modules": { "n_2c4a04b0445c5234": { "name": "Choose the next email", "imports": [], "source": "export default function next({events,previousState,createdId}) {\n if(!Array.isArray(events)||events.length>25)throw Error('Invalid Gmail page');\n for(const e of events)if(!e||typeof e.messageId!=='string'||!e.messageId)throw Error('Missing Gmail message ID');\n let index=0;if(previousState!=null){if(!Number.isInteger(previousState.index)||previousState.index<0||previousState.index>=events.length||!Number.isInteger(createdId)||createdId<=0)throw Error('Invalid opportunity result');index=previousState.index+1}\n return {state:{index},more:index\\s@]+@[^<>\\s@]+)(?:>|$)/);if(!m)throw Error('Sender email address missing');\n return {email:m[1],subject,body:messageText};\n}\n" }, "n_18f4b2189938556e": { "name": "Prepare the opportunity", "imports": [], "source": "export default function values({email,subject,summary}){\n if(typeof summary!=='string'||!summary.trim())throw Error('Summary is empty');\n return {values:{type:'opportunity',name:subject,email_from:email,description:summary}};\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }