{ "kind": "open-flow-workflow", "version": 1, "name": "Send Scheduled Congratulations from Sheets", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_5482ca77655bea6f": { "name": "Daily at 08:00 UTC", "kind": "cron", "cronTimes": [ { "type": "cron", "expression": "0 8 * * *", "timezone": "UTC" } ] }, "n_cd0c1d703d289f5c": { "inputs": { "spreadsheetId": { "kind": "value", "value": "YOUR_SPREADSHEET_ID" }, "range": { "kind": "value", "value": "'Congratulations Calendar'!A:E" }, "majorDimension": { "kind": "value", "value": "ROWS" }, "valueRenderOption": { "kind": "value", "value": "FORMATTED_VALUE" } }, "name": "Read congratulations calendar", "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 retrieve.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to retrieve." }, "nullable": false, "handle": "range" }, { "description": "The major dimension for the returned values.", "jsonSchema": { "type": "string", "enum": [ "DIMENSION_UNSPECIFIED", "ROWS", "COLUMNS" ], "description": "The major dimension for the returned values." }, "nullable": true, "handle": "majorDimension", "value": null }, { "description": "How values should be rendered in the output.", "jsonSchema": { "type": "string", "enum": [ "FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA" ], "description": "How values should be rendered in the output." }, "nullable": true, "handle": "valueRenderOption", "value": null }, { "description": "How dates and times should be rendered in the output.", "jsonSchema": { "type": "string", "enum": [ "SERIAL_NUMBER", "FORMATTED_STRING" ], "description": "How dates and times should be rendered in the output." }, "nullable": true, "handle": "dateTimeRenderOption", "value": null } ], "outputs": [ { "description": "The A1 notation of the range.", "jsonSchema": { "type": "string", "description": "The A1 notation of the range." }, "nullable": false, "handle": "range" }, { "description": "The major dimension of the values (ROWS or COLUMNS).", "jsonSchema": { "type": "string", "description": "The major dimension of the values (ROWS or COLUMNS)." }, "nullable": false, "handle": "majorDimension" }, { "description": "The data values in the range.", "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 values in the range." }, "nullable": false, "handle": "values" } ], "name": "Read congratulations calendar", "executor": { "kind": "connector", "action": "googlesheets.values_get" } } }, "n_dc5b1c95bfb1d9e5": { "inputs": { "spreadsheetId": { "kind": "value", "value": "YOUR_SPREADSHEET_ID" }, "range": { "kind": "value", "value": "'Congratulations Messages'!A:B" }, "majorDimension": { "kind": "value", "value": "ROWS" }, "valueRenderOption": { "kind": "value", "value": "FORMATTED_VALUE" } }, "name": "Read congratulations messages", "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 retrieve.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to retrieve." }, "nullable": false, "handle": "range" }, { "description": "The major dimension for the returned values.", "jsonSchema": { "type": "string", "enum": [ "DIMENSION_UNSPECIFIED", "ROWS", "COLUMNS" ], "description": "The major dimension for the returned values." }, "nullable": true, "handle": "majorDimension", "value": null }, { "description": "How values should be rendered in the output.", "jsonSchema": { "type": "string", "enum": [ "FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA" ], "description": "How values should be rendered in the output." }, "nullable": true, "handle": "valueRenderOption", "value": null }, { "description": "How dates and times should be rendered in the output.", "jsonSchema": { "type": "string", "enum": [ "SERIAL_NUMBER", "FORMATTED_STRING" ], "description": "How dates and times should be rendered in the output." }, "nullable": true, "handle": "dateTimeRenderOption", "value": null } ], "outputs": [ { "description": "The A1 notation of the range.", "jsonSchema": { "type": "string", "description": "The A1 notation of the range." }, "nullable": false, "handle": "range" }, { "description": "The major dimension of the values (ROWS or COLUMNS).", "jsonSchema": { "type": "string", "description": "The major dimension of the values (ROWS or COLUMNS)." }, "nullable": false, "handle": "majorDimension" }, { "description": "The data values in the range.", "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 values in the range." }, "nullable": false, "handle": "values" } ], "name": "Read congratulations messages", "executor": { "kind": "connector", "action": "googlesheets.values_get" } } }, "n_12f237067687bb43": { "inputs": { "calendar": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_cd0c1d703d289f5c", "output": "values" } ] }, "messages": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_dc5b1c95bfb1d9e5", "output": "values" } ] } }, "name": "Match and personalize greetings", "kind": "task", "task": { "inputs": [ { "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 values in the range." }, "nullable": false, "handle": "calendar" }, { "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 values in the range." }, "nullable": false, "handle": "messages" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "rows" } ], "name": "Match and personalize greetings", "moduleId": "n_5ebbde3b641fc003", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_761caaa3d5d2259d": { "inputs": { "rows": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_12f237067687bb43", "output": "rows" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_761caaa3d5d2259d", "output": "state" } ] } }, "name": "Prepare next greeting", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "rows" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string", "description": "The destination phone number in E.164 format." }, "nullable": false, "handle": "to" }, { "jsonSchema": { "type": "string", "description": "The text body of the outbound message." }, "nullable": false, "handle": "body" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "name": "Prepare next greeting", "moduleId": "n_e6b8c41475e2ae8d", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_0df7e385a934db09": { "inputs": {}, "name": "More greetings?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_761caaa3d5d2259d", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_eb08640a09d07aa3": { "inputs": { "to": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_761caaa3d5d2259d", "output": "to" } ] }, "from": { "kind": "value", "value": "YOUR_TWILIO_NUMBER" }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_761caaa3d5d2259d", "output": "body" } ] } }, "name": "Send greeting SMS", "kind": "task", "task": { "inputs": [ { "description": "The destination phone number in E.164 format.", "jsonSchema": { "type": "string", "description": "The destination phone number in E.164 format." }, "nullable": false, "handle": "to" }, { "description": "The Twilio phone number sending the message.", "jsonSchema": { "type": "string", "description": "The Twilio phone number sending the message." }, "nullable": false, "handle": "from" }, { "description": "The text body of the outbound message.", "jsonSchema": { "type": "string", "description": "The text body of the outbound message." }, "nullable": false, "handle": "body" } ], "outputs": [ { "description": "The Twilio message SID.", "jsonSchema": { "type": "string", "description": "The Twilio message SID." }, "nullable": false, "handle": "messageSid" }, { "description": "The Twilio account SID that owns the message.", "jsonSchema": { "type": [ "string", "null" ], "description": "The Twilio account SID that owns the message." }, "nullable": true, "handle": "accountSid" }, { "description": "The delivery status of the message.", "jsonSchema": { "type": [ "string", "null" ], "description": "The delivery status of the message." }, "nullable": true, "handle": "status" }, { "description": "The destination phone number.", "jsonSchema": { "type": [ "string", "null" ], "description": "The destination phone number." }, "nullable": true, "handle": "to" }, { "description": "The sender phone number.", "jsonSchema": { "type": [ "string", "null" ], "description": "The sender phone number." }, "nullable": true, "handle": "from" }, { "description": "The text body of the message.", "jsonSchema": { "type": [ "string", "null" ], "description": "The text body of the message." }, "nullable": true, "handle": "body" } ], "name": "Send greeting SMS", "executor": { "kind": "connector", "action": "twilio.send_message" } } }, "n_92091a6121bd94a5": { "name": "Greeting count", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "outputMappings": { "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_761caaa3d5d2259d", "output": "count" } ] } } } }, "edges": [ { "source": "n_5482ca77655bea6f", "target": "n_cd0c1d703d289f5c" }, { "source": "n_cd0c1d703d289f5c", "target": "n_dc5b1c95bfb1d9e5" }, { "source": "n_dc5b1c95bfb1d9e5", "target": "n_12f237067687bb43" }, { "source": "n_12f237067687bb43", "target": "n_761caaa3d5d2259d" }, { "source": "n_761caaa3d5d2259d", "target": "n_0df7e385a934db09" }, { "source": "n_0df7e385a934db09", "target": "n_eb08640a09d07aa3", "sourceHandle": "yes" }, { "source": "n_eb08640a09d07aa3", "target": "n_761caaa3d5d2259d" }, { "source": "n_0df7e385a934db09", "target": "n_92091a6121bd94a5", "sourceHandle": "otherwise" } ] } }, "modules": { "n_5ebbde3b641fc003": { "name": "Match and personalize greetings", "imports": [], "source": "export default function plan({calendar,messages}){\n const table=(v,needed)=>{if(!Array.isArray(v)||!v.length)return [];const h=v[0];if(!Array.isArray(h)||needed.some(x=>h.indexOf(x)<0||h.indexOf(x)!==h.lastIndexOf(x)))throw new Error('Sheet needs unique required headers');return v.slice(1).filter(r=>Array.isArray(r)&&r.some(x=>x!==''&&x!=null)).map(r=>Object.fromEntries(h.map((x,i)=>[x,r[i]??''])))};\n const people=table(calendar,['Date','Event Name','First Name','Phone Number']),templates=table(messages,['Event Name','Congratulations']);const now=new Date(),today=`${now.getUTCDate()}/${now.getUTCMonth()+1}`,rows=[];\n for(const person of people){if(String(person.Date)!==today)continue;const t=templates.findLast(x=>String(x['Event Name'])===String(person['Event Name']));if(!t||!t.Congratulations)continue;{const parts=String(t.Congratulations).split(','),first=parts.shift();if(!person['Phone Number'])throw new Error('A matched event needs a phone number');rows.push({to:String(person['Phone Number']),body:`${first} ${person['First Name']},${parts.join(',')}`})}}\n return {rows};\n}\n" }, "n_e6b8c41475e2ae8d": { "name": "Prepare next greeting", "imports": [], "source": "export default function next({rows,previousState}){const index=previousState===null?0:previousState.index+1;if(index>=rows.length)return {state:{index},more:false,to:'-',body:'-',count:rows.length};return {state:{index},more:true,to:rows[index].to,body:rows[index].body,count:rows.length}}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }