{ "kind": "open-flow-workflow", "version": 1, "name": "Send Monthly ProfitWell Metrics to Mattermost", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_da670cfd5f89e70b": { "name": "Monthly at 09:00 UTC", "kind": "cron", "cronTimes": [ { "type": "cron", "expression": "0 9 1 * *", "timezone": "UTC" } ] }, "n_d39885eb78968e9f": { "inputs": { "url": { "kind": "value", "value": "https://api.profitwell.com/v2/metrics/monthly/" }, "auth.token": { "kind": "unset" } }, "name": "Get monthly ProfitWell metrics", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "auth.token" } ], "outputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" } ], "name": "Get monthly ProfitWell metrics", "executor": { "method": "GET", "query": { "mode": "fields", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "none", "authentication": { "type": "apiKey", "name": "Authorization", "in": "header" }, "responseFormat": "json", "statusPolicy": "success", "kind": "http-request" } } }, "n_a652d6aae8c5f337": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d39885eb78968e9f", "output": "body" } ] }, "channelId": { "kind": "value", "value": "YOUR_MATTERMOST_CHANNEL_ID" } }, "name": "Format the monthly metrics", "kind": "task", "task": { "inputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "channelId" } ], "outputs": [ { "jsonSchema": { "type": "string", "minLength": 1, "description": "A Mattermost object ID." }, "nullable": false, "handle": "channelId" }, { "jsonSchema": { "type": "string", "minLength": 1, "description": "The Markdown message body to post." }, "nullable": false, "handle": "message" } ], "name": "Format the monthly metrics", "moduleId": "n_3741e87cae4e506d", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_d65d0e8e96c7b12f": { "inputs": { "channelId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_a652d6aae8c5f337", "output": "channelId" } ] }, "message": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_a652d6aae8c5f337", "output": "message" } ] } }, "name": "Post to Mattermost", "kind": "task", "task": { "inputs": [ { "description": "A Mattermost object ID.", "jsonSchema": { "type": "string", "minLength": 1, "description": "A Mattermost object ID." }, "nullable": false, "handle": "channelId" }, { "description": "The Markdown message body to post.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The Markdown message body to post." }, "nullable": false, "handle": "message" }, { "description": "Optional root post ID for replying in a thread.", "jsonSchema": { "type": "string", "minLength": 1, "description": "Optional root post ID for replying in a thread." }, "nullable": true, "handle": "rootId", "value": null }, { "description": "Optional Mattermost post props object.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "Optional Mattermost post props object." }, "nullable": true, "handle": "props", "value": null } ], "outputs": [ { "description": "A Mattermost post object.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A Mattermost post object." }, "nullable": false, "handle": "post" }, { "description": "The raw Mattermost API JSON response.", "jsonSchema": { "description": "The raw Mattermost API JSON response." }, "nullable": false, "handle": "raw" } ], "name": "Post to Mattermost", "executor": { "kind": "connector", "action": "mattermost.create_post" } } } }, "edges": [ { "source": "n_da670cfd5f89e70b", "target": "n_d39885eb78968e9f" }, { "source": "n_d39885eb78968e9f", "target": "n_a652d6aae8c5f337" }, { "source": "n_a652d6aae8c5f337", "target": "n_d65d0e8e96c7b12f" } ] } }, "modules": { "n_3741e87cae4e506d": { "name": "Format the monthly metrics", "imports": [], "source": "export default function format({body,channelId}) {\n if(typeof channelId!=='string'||!channelId.trim())throw Error('Set the Mattermost channel ID');\n if(!body||typeof body!=='object'||!body.data||typeof body.data!=='object')throw Error('Invalid ProfitWell response');\n const fields=[['Active Customers','active_customers'],['Trailing Customers','active_trialing_customers'],['New Customers','new_customers'],['Growth Rate','growth_rate'],['Recurring Revenue','recurring_revenue']];\n const lines=fields.map(([label,key])=>{const entries=body.data[key];if(!Array.isArray(entries)||!entries.length)throw Error('Missing monthly metric '+key);const last=entries[entries.length-1];if(!last||!(last.value===null||(typeof last.value==='number'&&Number.isFinite(last.value))))throw Error('Invalid monthly metric '+key);return label+': '+String(last.value)});\n return {channelId,message:lines.join('\\n')};\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }