{ "kind": "open-flow-workflow", "version": 1, "name": "Create and Update a SendGrid Contact", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_d8169f34029e2096": { "name": "Enter contact details", "kind": "start", "inputDefs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "email" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "firstName" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "lastName" } ] }, "n_38d41f5a172ef1f5": { "inputs": { "email": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d8169f34029e2096", "output": "email" } ] }, "firstName": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d8169f34029e2096", "output": "firstName" } ] }, "lastName": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d8169f34029e2096", "output": "lastName" } ] } }, "name": "Validate contact details", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "email" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "firstName" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "lastName" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "email" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "firstName" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "lastName" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "first" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "second" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "lookup" } ], "name": "Validate contact details", "moduleId": "n_09cf2e63bd00614f", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_a0598aeacb0e2535": { "inputs": { "endpoint": { "kind": "value", "value": "/v3/marketing/contacts" }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_38d41f5a172ef1f5", "output": "first" } ] } }, "name": "Add first name", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "endpoint" }, { "jsonSchema": {}, "nullable": true, "handle": "body" } ], "outputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" } ], "name": "Add first name", "executor": { "providerId": "sendgrid", "method": "PUT", "query": { "mode": "fields", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "json", "kind": "connector-proxy" } } }, "n_5ce6f587a682fea0": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_a0598aeacb0e2535", "output": "body" } ] }, "statusCode": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_a0598aeacb0e2535", "output": "statusCode" } ] }, "bodyEncoding": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_a0598aeacb0e2535", "output": "bodyEncoding" } ] } }, "name": "Read first job ID", "kind": "task", "task": { "inputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "jobId" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "endpoint" } ], "name": "Read first job ID", "moduleId": "n_16b96793cd21a12c", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_0e252b45419f98eb": { "inputs": {}, "name": "Wait for first job", "kind": "wait", "inputDefinitions": [], "prompt": "Wait for first job", "autoContinue": { "kind": "after", "durationMs": 5000 } }, "n_80efc9e6504b9aea": { "inputs": { "endpoint": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5ce6f587a682fea0", "output": "endpoint" } ] } }, "name": "Check first job", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "endpoint" } ], "outputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" } ], "name": "Check first job", "executor": { "providerId": "sendgrid", "method": "GET", "query": { "mode": "fields", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "none", "kind": "connector-proxy" } } }, "n_eed88c7ec69091ef": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_80efc9e6504b9aea", "output": "body" } ] }, "statusCode": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_80efc9e6504b9aea", "output": "statusCode" } ] }, "bodyEncoding": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_80efc9e6504b9aea", "output": "bodyEncoding" } ] }, "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_eed88c7ec69091ef", "output": "state" } ] } }, "name": "Check first job status", "kind": "task", "task": { "inputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previous" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "done" } ], "name": "Check first job status", "moduleId": "n_ff838409bfaa9ebd", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_bb3bc6450c5518f9": { "inputs": {}, "name": "First job complete?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "done", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_eed88c7ec69091ef", "output": "done" } }, "operator": "isTrue" } ] } ] } ] }, "n_30298d5e7b840fef": { "inputs": { "endpoint": { "kind": "value", "value": "/v3/marketing/contacts" }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_38d41f5a172ef1f5", "output": "second" } ] } }, "name": "Add last name", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "endpoint" }, { "jsonSchema": {}, "nullable": true, "handle": "body" } ], "outputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" } ], "name": "Add last name", "executor": { "providerId": "sendgrid", "method": "PUT", "query": { "mode": "fields", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "json", "kind": "connector-proxy" } } }, "n_022936f36479e03b": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_30298d5e7b840fef", "output": "body" } ] }, "statusCode": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_30298d5e7b840fef", "output": "statusCode" } ] }, "bodyEncoding": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_30298d5e7b840fef", "output": "bodyEncoding" } ] } }, "name": "Read second job ID", "kind": "task", "task": { "inputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "jobId" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "endpoint" } ], "name": "Read second job ID", "moduleId": "n_d9a44324bcd50beb", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_f1cefdab1a4cd1d0": { "inputs": {}, "name": "Wait for second job", "kind": "wait", "inputDefinitions": [], "prompt": "Wait for second job", "autoContinue": { "kind": "after", "durationMs": 5000 } }, "n_9c63732b7c35d8d2": { "inputs": { "endpoint": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_022936f36479e03b", "output": "endpoint" } ] } }, "name": "Check second job", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "endpoint" } ], "outputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" } ], "name": "Check second job", "executor": { "providerId": "sendgrid", "method": "GET", "query": { "mode": "fields", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "none", "kind": "connector-proxy" } } }, "n_4b5bdd887e0b41c1": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_9c63732b7c35d8d2", "output": "body" } ] }, "statusCode": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_9c63732b7c35d8d2", "output": "statusCode" } ] }, "bodyEncoding": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_9c63732b7c35d8d2", "output": "bodyEncoding" } ] }, "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_4b5bdd887e0b41c1", "output": "state" } ] } }, "name": "Check second job status", "kind": "task", "task": { "inputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previous" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "done" } ], "name": "Check second job status", "moduleId": "n_a66ef7b8b35a1c6b", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_8b6fff522ff9e79e": { "inputs": {}, "name": "Second job complete?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "done", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_4b5bdd887e0b41c1", "output": "done" } }, "operator": "isTrue" } ] } ] } ] }, "n_2c85be3d4ba7fe63": { "inputs": { "endpoint": { "kind": "value", "value": "/v3/marketing/contacts/search/identifiers/email" }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_38d41f5a172ef1f5", "output": "lookup" } ] } }, "name": "Read the contact", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "endpoint" }, { "jsonSchema": {}, "nullable": true, "handle": "body" } ], "outputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" } ], "name": "Read the contact", "executor": { "providerId": "sendgrid", "method": "POST", "query": { "mode": "fields", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "json", "kind": "connector-proxy" } } }, "n_60e1dc1761489dda": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2c85be3d4ba7fe63", "output": "body" } ] }, "statusCode": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2c85be3d4ba7fe63", "output": "statusCode" } ] }, "bodyEncoding": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2c85be3d4ba7fe63", "output": "bodyEncoding" } ] }, "email": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_38d41f5a172ef1f5", "output": "email" } ] }, "firstName": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_38d41f5a172ef1f5", "output": "firstName" } ] }, "lastName": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_38d41f5a172ef1f5", "output": "lastName" } ] } }, "name": "Verify the contact", "kind": "task", "task": { "inputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "email" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "firstName" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "lastName" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "contact" } ], "name": "Verify the contact", "moduleId": "n_8c4106935661d1e8", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_02085cb3eaa0dec2": { "name": "Saved contact", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "contact" } ], "outputMappings": { "contact": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_60e1dc1761489dda", "output": "contact" } ] } } } }, "edges": [ { "source": "n_d8169f34029e2096", "target": "n_38d41f5a172ef1f5" }, { "source": "n_38d41f5a172ef1f5", "target": "n_a0598aeacb0e2535" }, { "source": "n_a0598aeacb0e2535", "target": "n_5ce6f587a682fea0" }, { "source": "n_5ce6f587a682fea0", "target": "n_0e252b45419f98eb" }, { "source": "n_0e252b45419f98eb", "target": "n_80efc9e6504b9aea", "sourceHandle": "continue" }, { "source": "n_80efc9e6504b9aea", "target": "n_eed88c7ec69091ef" }, { "source": "n_eed88c7ec69091ef", "target": "n_bb3bc6450c5518f9" }, { "source": "n_bb3bc6450c5518f9", "target": "n_30298d5e7b840fef", "sourceHandle": "done" }, { "source": "n_bb3bc6450c5518f9", "target": "n_0e252b45419f98eb", "sourceHandle": "otherwise" }, { "source": "n_30298d5e7b840fef", "target": "n_022936f36479e03b" }, { "source": "n_022936f36479e03b", "target": "n_f1cefdab1a4cd1d0" }, { "source": "n_f1cefdab1a4cd1d0", "target": "n_9c63732b7c35d8d2", "sourceHandle": "continue" }, { "source": "n_9c63732b7c35d8d2", "target": "n_4b5bdd887e0b41c1" }, { "source": "n_4b5bdd887e0b41c1", "target": "n_8b6fff522ff9e79e" }, { "source": "n_8b6fff522ff9e79e", "target": "n_2c85be3d4ba7fe63", "sourceHandle": "done" }, { "source": "n_8b6fff522ff9e79e", "target": "n_f1cefdab1a4cd1d0", "sourceHandle": "otherwise" }, { "source": "n_2c85be3d4ba7fe63", "target": "n_60e1dc1761489dda" }, { "source": "n_60e1dc1761489dda", "target": "n_02085cb3eaa0dec2" } ] } }, "modules": { "n_09cf2e63bd00614f": { "name": "Validate contact details", "imports": [], "source": "export default function input({email,firstName,lastName}){if(typeof email!=='string'||email.length>254||!/^\\S+@\\S+\\.\\S+$/.test(email.trim()))throw Error('Enter a valid email');if(typeof firstName!=='string'||!firstName.trim()||firstName.length>50||typeof lastName!=='string'||!lastName.trim()||lastName.length>50)throw Error('Enter first and last names (up to 50 characters)');email=email.trim().toLowerCase();return {email,firstName:firstName.trim(),lastName:lastName.trim(),first:{contacts:[{email,first_name:firstName.trim()}]},second:{contacts:[{email,last_name:lastName.trim()}]},lookup:{identifiers:[email]}};}\n" }, "n_16b96793cd21a12c": { "name": "Read first job ID", "imports": [], "source": "export default function job({body,statusCode,bodyEncoding}){if(statusCode!==202||bodyEncoding||typeof body?.job_id!=='string'||!body.job_id)throw Error('Contact write did not return a job ID');return {jobId:body.job_id,endpoint:'/v3/marketing/contacts/imports/'+encodeURIComponent(body.job_id)};}\n" }, "n_ff838409bfaa9ebd": { "name": "Check first job status", "imports": [], "source": "export default function check({body,statusCode,bodyEncoding,previous}){if(statusCode!==200||bodyEncoding||!body||typeof body!=='object')throw Error('Could not read contact job');const n=(previous?.count??0)+1;if(!Number.isInteger(n)||n<1||n>12)throw Error('Invalid job state');if(body.status==='completed')return {state:{count:n},done:true};if(body.status==='pending'){if(n>=12)throw Error('Contact job still pending after 12 checks');return {state:{count:n},done:false};}throw Error('Contact job failed or returned an unknown status');}\n" }, "n_d9a44324bcd50beb": { "name": "Read second job ID", "imports": [], "source": "export default function job({body,statusCode,bodyEncoding}){if(statusCode!==202||bodyEncoding||typeof body?.job_id!=='string'||!body.job_id)throw Error('Contact write did not return a job ID');return {jobId:body.job_id,endpoint:'/v3/marketing/contacts/imports/'+encodeURIComponent(body.job_id)};}\n" }, "n_a66ef7b8b35a1c6b": { "name": "Check second job status", "imports": [], "source": "export default function check({body,statusCode,bodyEncoding,previous}){if(statusCode!==200||bodyEncoding||!body||typeof body!=='object')throw Error('Could not read contact job');const n=(previous?.count??0)+1;if(!Number.isInteger(n)||n<1||n>12)throw Error('Invalid job state');if(body.status==='completed')return {state:{count:n},done:true};if(body.status==='pending'){if(n>=12)throw Error('Contact job still pending after 12 checks');return {state:{count:n},done:false};}throw Error('Contact job failed or returned an unknown status');}\n" }, "n_8c4106935661d1e8": { "name": "Verify the contact", "imports": [], "source": "export default function confirm({body,statusCode,bodyEncoding,email,firstName,lastName}){if(statusCode!==200||bodyEncoding)throw Error('Could not read contact');const c=body?.result?.[email]?.contact;if(!c||typeof c.id!=='string'||c.email?.toLowerCase()!==email||c.first_name!==firstName||c.last_name!==lastName)throw Error('Contact does not match the requested update');return {contact:c};}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }