{ "kind": "open-flow-workflow", "version": 1, "name": "Customer Feedback Sentiment", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_d9c3331cb424a68f": { "name": "Feedback submitted", "kind": "webhook", "bodyFields": [ { "jsonSchema": {}, "nullable": true, "handle": "Name" }, { "jsonSchema": {}, "nullable": true, "handle": "What is your feedback about?" }, { "jsonSchema": {}, "nullable": true, "handle": "Your feedback" }, { "jsonSchema": {}, "nullable": true, "handle": "How do we get in touch with you?" }, { "jsonSchema": {}, "nullable": true, "handle": "submittedAt" } ], "method": "POST", "options": { "responseData": "{\"message\":\"Feedback was received.\"}", "responseHeaders": { "content-type": "application/json" } } }, "n_02b18ada6c9e0003": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d9c3331cb424a68f", "output": "body" } ] } }, "name": "Prepare feedback submission", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "body" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "name" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "category" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "feedback" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "contact" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "timestamp" } ], "name": "Prepare feedback submission", "moduleId": "n_001a962da0ead0c4", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_d005e90216db7ba9": { "inputs": { "feedback": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_02b18ada6c9e0003", "output": "feedback" } ] } }, "name": "Classify feedback sentiment", "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": "user", "content": "Classify the sentiment in the following customer feedback: {{feedback}}" } ] }, { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "model", "value": { "model": "gpt-4o-mini" } }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "feedback" } ], "outputs": [ { "description": "Generated response.", "jsonSchema": { "type": "string" }, "nullable": false, "handle": "output" } ], "name": "Classify feedback sentiment", "executor": { "kind": "llm", "mode": "chat" } } }, "n_b8d4a6a9f7a22a37": { "inputs": { "name": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_02b18ada6c9e0003", "output": "name" } ] }, "category": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_02b18ada6c9e0003", "output": "category" } ] }, "feedback": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_02b18ada6c9e0003", "output": "feedback" } ] }, "contact": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_02b18ada6c9e0003", "output": "contact" } ] }, "timestamp": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_02b18ada6c9e0003", "output": "timestamp" } ] }, "sentiment": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d005e90216db7ba9", "output": "output" } ] } }, "name": "Prepare feedback row", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "name" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "category" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "feedback" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "contact" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "timestamp" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "sentiment" } ], "outputs": [ { "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" } ], "name": "Prepare feedback row", "moduleId": "n_571e822b51e9a4db", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_314f32075440014b": { "inputs": { "spreadsheetId": { "kind": "value", "value": "YOUR_SPREADSHEET_ID" }, "range": { "kind": "value", "value": "'Feedback'!A:H" }, "values": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_b8d4a6a9f7a22a37", "output": "values" } ] }, "valueInputOption": { "kind": "value", "value": "RAW" }, "insertDataOption": { "kind": "value", "value": "INSERT_ROWS" } }, "name": "Save classified feedback", "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 classified feedback", "executor": { "kind": "connector", "action": "googlesheets.spreadsheets_values_append" } } } }, "edges": [ { "source": "n_d9c3331cb424a68f", "target": "n_02b18ada6c9e0003" }, { "source": "n_02b18ada6c9e0003", "target": "n_d005e90216db7ba9" }, { "source": "n_d005e90216db7ba9", "target": "n_b8d4a6a9f7a22a37" }, { "source": "n_b8d4a6a9f7a22a37", "target": "n_314f32075440014b" } ] } }, "modules": { "n_001a962da0ead0c4": { "name": "Prepare feedback submission", "imports": [], "source": "export default function prepareFeedback({ body }) {\n if (!body || typeof body !== 'object' || Array.isArray(body)) throw new Error('Feedback must be a JSON object.');\n const required = ['Name', 'What is your feedback about?', 'Your feedback'];\n for (const name of required) if (typeof body[name] !== 'string' || !body[name].trim()) throw new Error('Required feedback field: ' + name);\n const category = body['What is your feedback about?'];\n if (!['Product', 'Service', 'Other'].includes(category)) throw new Error('Feedback category must be Product, Service, or Other.');\n const contact = body['How do we get in touch with you?'];\n if (contact != null && typeof contact !== 'string') throw new Error('Contact must be text.');\n const timestamp = body.submittedAt || new Date().toISOString();\n if (typeof timestamp !== 'string' || !Number.isFinite(Date.parse(timestamp))) throw new Error('Submission timestamp must be a valid date.');\n return { name: body.Name, category, feedback: body['Your feedback'], contact: contact || '', timestamp };\n}\n" }, "n_571e822b51e9a4db": { "name": "Prepare feedback row", "imports": [], "source": "export default function feedbackRow({ name, category, feedback, contact, timestamp, sentiment }) {\n if (typeof sentiment !== 'string' || !sentiment.trim()) throw new Error('The sentiment model returned an empty response.');\n return { values: [[timestamp, category, feedback, name, contact, 'Form', '', sentiment]] };\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }