Custom Action for Salesforce Marketing Cloud
Marketing Cloud Custom Action
The flow provides a boilerplate to create a custom action for consumption in Salesforce Marketing Cloud. The flow outputs the data received from marketing cloud in a debug node. So you need to add whatever you want to do at the level of that debug node
Setup
- You need to open the the
Test JWT
node and edit the secret. Better: remove it and set the environment variable as described in the node's documentation - the endpoint path is /endpoints/demo. If you want to change that you need to edit 2 nodes:
config.json
andAction Node
Feedback
Always welcome. Twitter @notessensei
[{"id":"f013e682.3118f8","type":"tab","label":"Marketing Cloud","disabled":false,"info":""},{"id":"a3b117e3.89e008","type":"http response","z":"f013e682.3118f8","name":"","statusCode":"","headers":{},"x":670,"y":360,"wires":[]},{"id":"f5dffd18.e3d34","type":"http in","z":"f013e682.3118f8","name":"Configuration node","url":"/marketing-cloud/config.json","method":"get","upload":false,"swaggerDoc":"","x":130,"y":360,"wires":[["6b8f953b.a15e5c"]]},{"id":"c75cc7a8.cfdec8","type":"comment","z":"f013e682.3118f8","name":"Config File","info":"The JSON file that explains to Marketing Cloud what\nthe activity does","x":100,"y":320,"wires":[]},{"id":"6b8f953b.a15e5c","type":"function","z":"f013e682.3118f8","name":"config.json","func":"let activity = \"demo\";\nlet detailpayload = {\n params : msg.req.params,\n headers : msg.req.headers\n};\nlet host = msg.req.headers.host;\nlet hostURL = (host.substring(0,9)==='localhost') ? 'http://'+host : 'https://'+host;\n\nmsg.payload = {\n \"workflowApiVersion\": \"1.1\",\n \"metaData\": {\n \"icon\": \"/red/images/node-red.png\",\n \"category\": \"customer\",\n \"configOnDrop\": true\n },\n \"type\": \"REST\",\n \"lang\": {\n \"en-US\": {\n \"name\": activity,\n \"description\":activity,\n \"step1Label\": \"Configure Activity\"\n }\n },\n \"arguments\": {\n \"execute\": {\n \"inArguments\": [\n {\n \"CampaignID\": \"\",\n \"ContactKey\": \"{{Contact.Key}}\"\n }\n ],\n \"outArguments\": [\n {\n \"status\": \"\"\n }\n ],\n \"url\": hostURL+\"/endpoints/\"+activity+\"/execute\",\n \"verb\": \"POST\",\n \"body\": \"\",\n \"header\": \"\",\n \"format\": \"json\",\n \"useJwt\": true,\n \"timeout\": 10000\n }\n },\n \"configurationArguments\": {\n \"applicationExtensionKey\": \"mc_uniquekey\",\n \"save\": {\n \"url\": hostURL+\"/endpoints/\"+activity+\"/save\",\n \"verb\": \"POST\",\n \"useJwt\": true\n },\n \"publish\": {\n \"url\": hostURL+\"/endpoints/\"+activity+\"/publish\",\n \"verb\": \"POST\",\n \"useJwt\": true\n },\n \"stop\": {\n \"url\": hostURL+\"/endpoints/\"+activity+\"/stop\",\n \"verb\": \"POST\",\n \"useJwt\": true\n },\n \"validate\": {\n \"url\": hostURL+\"/endpoints/\"+activity+\"/validate\",\n \"verb\": \"POST\",\n \"useJwt\": true\n }\n },\n \"wizardSteps\": [\n {\n \"label\": \"Configure Activity\",\n \"key\": \"step1\"\n }\n ],\n \"userInterfaces\": {\n \"configModal\": {\n \"height\": 500,\n \"width\": 520,\n \"fullscreen\": false\n }\n },\n \"schema\": {\n \"arguments\": {\n \"execute\": {\n \"inArguments\": [],\n \"outArguments\": [\n {\n \"status\": {\n \"dataType\": \"Text\",\n \"direction\": \"out\",\n \"access\": \"visible\"\n }\n }\n ]\n }\n }\n },\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":410,"y":360,"wires":[["a3b117e3.89e008"]],"info":"# Configuration\n\nYou need to adjust the `activity` variable to the \nsame value you specify in the \"Action Node\".\nDefault is \"demo\""},{"id":"c5c130fa.e4b04","type":"http response","z":"f013e682.3118f8","name":"","statusCode":"","headers":{},"x":770,"y":660,"wires":[]},{"id":"44a433d4.4ff93c","type":"http in","z":"f013e682.3118f8","name":"Action Node","url":"/endpoints/demo/:action","method":"post","upload":false,"swaggerDoc":"","x":110,"y":680,"wires":[["32b72ce.89070d4"]]},{"id":"979d4b98.cd9b28","type":"comment","z":"f013e682.3118f8","name":"Incoming from Marketing Cloud","info":"","x":170,"y":640,"wires":[]},{"id":"449433f6.0f9d3c","type":"switch","z":"f013e682.3118f8","name":"Determine action","property":"req.params.action","propertyType":"msg","rules":[{"t":"eq","v":"save","vt":"str"},{"t":"eq","v":"publish","vt":"str"},{"t":"eq","v":"stop","vt":"str"},{"t":"eq","v":"validate","vt":"str"},{"t":"else"},{"t":"eq","v":"execute","vt":"str"}],"checkall":"false","repair":false,"outputs":6,"x":330,"y":740,"wires":[["739eaf33.de7e2"],["739eaf33.de7e2"],["739eaf33.de7e2"],["739eaf33.de7e2"],["de462a32.264b48"],["4d7d3ac9.4f4da4","6fddb92b.d54758"]]},{"id":"de462a32.264b48","type":"template","z":"f013e682.3118f8","name":"Error Message","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"\"{ status : 'Error' }\"","output":"str","x":580,"y":720,"wires":[["2ff490b7.e7f19"]]},{"id":"2ff490b7.e7f19","type":"http response","z":"f013e682.3118f8","name":"Not Found","statusCode":"404","headers":{},"x":790,"y":720,"wires":[]},{"id":"4d7d3ac9.4f4da4","type":"template","z":"f013e682.3118f8","name":"Execute status","field":"payload","fieldType":"msg","format":"text","syntax":"mustache","template":"'{\"status\" : \"OK\"}'","output":"str","x":580,"y":780,"wires":[["157cb491.436b7b"]]},{"id":"32b72ce.89070d4","type":"jwt verify","z":"f013e682.3118f8","name":"Test JWT","alg":["HS256"],"jwkurl":"","secret":"Barbequeue","key":"","signvar":"payload","storetoken":"token","x":140,"y":740,"wires":[["449433f6.0f9d3c"]]},{"id":"beca8ce7.95c0a","type":"debug","z":"f013e682.3118f8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":470,"y":1000,"wires":[]},{"id":"120fbb23.92a515","type":"inject","z":"f013e682.3118f8","name":"","topic":"","payload":"eyJhbGciOiJIUzI1NiJ9.ewogICAiaW5Bcmd1bWVudHMiIDogW3sKICAgICJDYW1wYWlnbklEIjogIjcwMTdGMDAwMDAwTUo0T1FBVyIsCgkiQ29udGFjdEtleSI6ICIwMDM3RjAwMDAwejhWYnpRQUUiCiAgIH1dCn0.gnjf3FN7HMydDWUm-NMn3gaKpvuuXpEMrunR1A2PAgA","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":1000,"wires":[["30901b69.d485a4"]]},{"id":"30901b69.d485a4","type":"http request","z":"f013e682.3118f8","name":"","method":"POST","ret":"txt","paytoqs":false,"url":"http://localhost:1880/endpoints/demo/execute","tls":"","proxy":"","authType":"basic","x":290,"y":1000,"wires":[["beca8ce7.95c0a"]]},{"id":"739eaf33.de7e2","type":"template","z":"f013e682.3118f8","name":"Simple return","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{ \"action\" : \"{{req.params.action}}\",\n \"status\" : \"OK\" \n}","output":"json","x":580,"y":660,"wires":[["c5c130fa.e4b04"]]},{"id":"954c450b.17ecc8","type":"comment","z":"f013e682.3118f8","name":"This can be used for testing","info":"","x":160,"y":960,"wires":[]},{"id":"8ab679d8.15e868","type":"comment","z":"f013e682.3118f8","name":"Static files (html, css, json)","info":"","x":150,"y":420,"wires":[]},{"id":"157cb491.436b7b","type":"http response","z":"f013e682.3118f8","name":"","statusCode":"","headers":{},"x":770,"y":780,"wires":[]},{"id":"6fddb92b.d54758","type":"debug","z":"f013e682.3118f8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":490,"y":900,"wires":[]},{"id":"8bb75e68.fec1a","type":"comment","z":"f013e682.3118f8","name":"Actual action goes here - whatever it might be","info":"","x":210,"y":880,"wires":[],"icon":"node-red/arrow-in.png"},{"id":"b8f1eb7f.067bf8","type":"http response","z":"f013e682.3118f8","name":"html","statusCode":"","headers":{"content-type":"text/html"},"x":690,"y":500,"wires":[]},{"id":"a0460c24.e8f65","type":"http in","z":"f013e682.3118f8","name":"/marketing-cloud/index.html","url":"/marketing-cloud/index.html","method":"get","upload":false,"swaggerDoc":"","x":160,"y":500,"wires":[["e6a1e098.42916"]]},{"id":"e6a1e098.42916","type":"template","z":"f013e682.3118f8","name":"static HTML","field":"payload","fieldType":"msg","format":"html","syntax":"plain","template":"<!DOCTYPE html>\n<html>\n <head>\n <title></title>\n <meta charset=\"utf-8\" />\n <meta http-equiv=\"x-ua-compatible\" content=\"ie=edge\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <meta http-equiv=\"Content-Security-Policy\" content=\"upgrade-insecure-requests\" />\n <script type=\"text/javascript\" src=\"http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js\"></script>\n <script type=\"text/javascript\" src=\"js/postmonger.js\"></script>\n <script type=\"text/javascript\">\n var mc_fuel_token = '';\n var connection = new Postmonger.Session();\n var payload = {};\n var steps = [{ label: 'Configure Postcard', key: 'step1' }];\n\n $(window).ready(onRender);\n\n connection.on('initActivity', initialize);\n connection.on('requestedTokens', onGetTokens);\n connection.on('requestedEndpoints', onGetEndpoints);\n connection.on('clickedNext', save);\n\n function onRender() {\n connection.trigger('ready');\n connection.trigger('requestTokens');\n connection.trigger('requestEndpoints');\n }\n\n function jsonCallback(data) {}\n\n function initialize(data) {\n if (data) {\n payload = data;\n }\n\n var hasInArguments = Boolean(\n payload['arguments'] &&\n payload['arguments'].execute &&\n payload['arguments'].execute.inArguments &&\n payload['arguments'].execute.inArguments.length > 0\n );\n\n if (hasInArguments && payload['arguments'].execute.inArguments[0].CampaignID) {\n $('#CampaignID').val(payload['arguments'].execute.inArguments[0].CampaignID);\n }\n\n connection.trigger('updateButton', {\n button: 'next',\n text: 'done',\n visible: true\n });\n }\n\n function onGetTokens(tokens) {\n if (tokens != null && tokens.fuel2token != null) {\n mc_fuel_token = tokens.fuel2token;\n }\n }\n\n function onGetEndpoints(endpoints) {}\n\n function save() {\n $('#errorMsgPreview').text('');\n\n var CampaignID = $('#CampaignID').val();\n var errMsg;\n\n var hasInArguments = Boolean(\n payload['arguments'] &&\n payload['arguments'].execute &&\n payload['arguments'].execute.inArguments &&\n payload['arguments'].execute.inArguments.length > 0\n );\n if (hasInArguments) {\n payload['arguments'].execute.inArguments[0].CampaignID = CampaignID;\n }\n\n if (CampaignID.length == 0) {\n errMsg = 'Error: Campaign ID cannot be blank.';\n console.error(errMsg);\n $('#errorMsgConfig').text(errMsg);\n connection.trigger('ready');\n } else {\n payload['metaData'].isConfigured = true;\n connection.trigger('updateActivity', payload);\n }\n }\n </script>\n <link rel=\"stylesheet\" type=\"text/css\" href=\"css/styles.css\" />\n </head>\n <body>\n <div id=\"step1\" class=\"step\">\n <div class=\"col-xs-9\">\n <h3>Campaign Association</h3>\n <div class=\"well\">\n <fieldset>\n <div>\n <font color=\"red\"><div id=\"errorMsgConfig\"></div></font>\n </div>\n <!-- TODO: Call back to Salesforce ?-->\n <div class=\"form-group\">\n <label for=\"cid\" class=\"label\">Campaign ID</label>\n <div class=\"col-lg-8\">\n <input type=\"text\" id=\"CampaignID\" />\n </div>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n </body>\n</html>\n","output":"str","x":470,"y":500,"wires":[["b8f1eb7f.067bf8"]]},{"id":"23a152b9.437fee","type":"http response","z":"f013e682.3118f8","name":"css","statusCode":"","headers":{"content-type":"text/css"},"x":690,"y":540,"wires":[]},{"id":"b758f1b7.53d34","type":"http in","z":"f013e682.3118f8","name":"/marketing-cloud/css/style.css","url":"/marketing-cloud/css/style.css","method":"get","upload":false,"swaggerDoc":"","x":170,"y":540,"wires":[["b4196642.0d04c8"]]},{"id":"b4196642.0d04c8","type":"template","z":"f013e682.3118f8","name":"static CSS","field":"payload","fieldType":"msg","format":"css","syntax":"plain","template":"body {\n margin: 20px;\n padding: 0px;\n\tfont-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;\n\tfont-size:12px;\n}\n\nform {\n\twidth:400px;\n}\n\nselect\n{\n\tpadding: 5px;\n}\n\ntextarea, select, input{\n\twidth:400px;\n}\n\ndiv.isPush\n{\n\tpadding-bottom:10px;\n}\n\ninput[type='radio']\n{\n\twidth:20px;\n}\n\ninput[type='checkbox']\n{\n\twidth: 20px;\n\tborder:solid 1px red;\n}\n\ntd{\n\tvertical-align:top;\t\n}\n\ntd.label\n{\n\tpadding-top:2px;\n\twidth:120px;\n}\n\n#container {\n margin: 30px;\n}\n","output":"str","x":470,"y":540,"wires":[["23a152b9.437fee"]]},{"id":"268afbe1.895354","type":"http response","z":"f013e682.3118f8","name":"","statusCode":"","headers":{"content-type":"text/javascript"},"x":690,"y":580,"wires":[]},{"id":"80b620de.65089","type":"http in","z":"f013e682.3118f8","name":"/marketing-cloud/js/postmonger.js","url":"/marketing-cloud/js/postmonger.js","method":"get","upload":false,"swaggerDoc":"","x":180,"y":580,"wires":[["64b99f72.1a599"]]},{"id":"64b99f72.1a599","type":"template","z":"f013e682.3118f8","name":"postmonger.js","field":"payload","fieldType":"msg","format":"javascript","syntax":"plain","template":"/*\n * Postmonger.js version 0.0.14\n * https://github.com/kevinparkerson/postmonger\n *\n * Copyright (c) 2012-2014 Kevin Parkerson\n * Available via the MIT or new BSD license.\n * Further details and documentation:\n * http://kevinparkerson.github.com/postmonger/\n *\n *///\n\n(function (root, factory) {\n\tif (typeof define === 'function' && define.amd) {\n\t\tdefine('postmonger', [], function () { return factory(root); });\n\t} else if (typeof exports === 'object') {\n\t\tmodule.exports = factory(root);\n\t} else {\n\t\t// OR use browser globals if AMD is not present\n\t\troot.Postmonger = factory(root);\n\t}\n}(this, function (root) {\n\troot = root || window;\n\n\tvar exports = exports || undefined;\n\tvar Postmonger;\n\tvar previous = root.Postmonger;\n\tvar _window = (root.addEventListener || root.attachEvent) ? root : window;\n\tvar Connection, Events, Session;\n\n\t//Set up Postmonger namespace, provide noConflict support, and version\n\tif (typeof(exports) !== 'undefined') {\n\t\tPostmonger = exports;\n\t} else {\n\t\tPostmonger = {};\n\t}\n\tPostmonger.noConflict = function () {\n\t\troot.Postmonger = previous;\n\t\treturn this;\n\t};\n\tPostmonger.version = '0.0.14';\n\n\t//Create a new Postmonger Connection\n\tConnection = Postmonger.Connection = function (options) {\n\t\toptions = (typeof(options) === 'object') ? options : {};\n\n\t\tvar connect = options.connect || _window.parent;\n\t\tvar from = options.from || '*';\n\t\tvar to = options.to || '*';\n\t\tvar self = this;\n\n\t\t//If string, grab based on id\n\t\tif (typeof(connect) === 'string') {\n\t\t\tconnect = document.getElementById(connect);\n\t\t}\n\n\t\t//If no connection, check for jquery object\n\t\tif (connect && !connect.postMessage && connect.jquery) {\n\t\t\tconnect = connect.get(0);\n\t\t}\n\n\t\t//If still no connection, check for iframe\n\t\tif (connect && !connect.postMessage && (connect.contentWindow || connect.contentDocument)) {\n\t\t\tconnect = connect.contentWindow || connect.contentDocument;\n\t\t}\n\n\t\t//Throw warning if connection could not be made\n\t\tif (!(connect && connect.postMessage)) {\n\t\t\tif (_window.console && _window.console.warn) {\n\t\t\t\t_window.console.warn(' Warning: Postmonger could not establish connection with ', options.connect);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tself.connect = connect;\n\t\tself.to = to;\n\t\tself.from = from;\n\n\t\treturn self;\n\t};\n\n\t//Postmonger.Events - Hacked together from Backbone.Events and two Underscore functions.\n\tEvents = Postmonger.Events = function () {\n\t\tvar eventSplitter = /\\s+/;\n\t\tvar self = this;\n\n\t\tself._callbacks = {};\n\n\t\tself._has = function (obj, key) {\n\t\t\treturn Object.prototype.hasOwnProperty.call(obj, key);\n\t\t};\n\n\t\tself._keys = function (obj) {\n\t\t\tif (Object.keys) {\n\t\t\t\treturn Object.keys(obj);\n\t\t\t}\n\n\t\t\tif (typeof(obj)!=='object') {\n\t\t\t\tthrow new TypeError('Invalid object');\n\t\t\t}\n\n\t\t\tvar keys = [];\n\n\t\t\tfor (var key in obj) {\n\t\t\t\tif (self._has(obj, key)) {\n\t\t\t\t\tkeys[keys.length] = key;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn keys;\n\t\t};\n\n\t\tself.on = function (events, callback, context) {\n\t\t\tvar calls, event, node, tail, list;\n\n\t\t\tif (!callback) {\n\t\t\t\treturn self;\n\t\t\t}\n\n\t\t\tevents = events.split(eventSplitter);\n\n\t\t\tself._callbacks = self._callbacks || {};\n\t\t\tcalls = self._callbacks;\n\n\t\t\twhile (event = events.shift()) {\n\t\t\t\tlist = calls[event];\n\n\t\t\t\tnode = (list) ? list.tail : {};\n\t\t\t\ttail = {};\n\n\t\t\t\tnode.next = tail;\n\t\t\t\tnode.context = context;\n\t\t\t\tnode.callback = callback;\n\n\t\t\t\tcalls[event] = {\n\t\t\t\t\ttail: tail,\n\t\t\t\t\tnext: (list) ? list.next : node\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn self;\n\t\t};\n\n\t\tself.off = function (events, callback, context) {\n\t\t\tvar calls = self._callbacks;\n\t\t\tvar event, node, tail, cb, ctx;\n\n\t\t\tif (!calls) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!(events || callback || context)) {\n\t\t\t\tdelete self._callbacks;\n\t\t\t\treturn self;\n\t\t\t}\n\n\t\t\tevents = (events) ? events.split(eventSplitter) : self._keys(calls);\n\n\t\t\twhile (event = events.shift()) {\n\t\t\t\tnode = calls[event];\n\t\t\t\tdelete calls[event];\n\t\t\t\tif (!node || !(callback || context)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\ttail = node.tail;\n\t\t\t\twhile ((node = node.next) !== tail) {\n\t\t\t\t\tcb = node.callback;\n\t\t\t\t\tctx = node.context;\n\t\t\t\t\tif (((callback && cb) !== callback) || ((context && ctx) !== context)) {\n\t\t\t\t\t\tself.on(event, cb, ctx);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn self;\n\t\t};\n\n\t\tself.trigger = function (events) {\n\t\t\tvar event, node, calls, tail, args, all, rest;\n\n\t\t\tif (!(calls = self._callbacks)) {\n\t\t\t\treturn self;\n\t\t\t}\n\n\t\t\tall = calls.all;\n\t\t\tevents = events.split(eventSplitter);\n\t\t\trest = Array.prototype.slice.call(arguments, 1);\n\n\t\t\twhile (event = events.shift()) {\n\t\t\t\tif (node = calls[event]) {\n\t\t\t\t\ttail = node.tail;\n\t\t\t\t\twhile ((node = node.next) !== tail) {\n\t\t\t\t\t\tnode.callback.apply(node.context || self, rest);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (node = all) {\n\t\t\t\t\ttail = node.tail;\n\t\t\t\t\targs = [event].concat(rest);\n\t\t\t\t\twhile ((node = node.next) !== tail) {\n\t\t\t\t\t\tnode.callback.apply(node.context || self, args);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn self;\n\t\t};\n\n\t\treturn self;\n\t};\n\n\t//Create a new Postmonger Session\n\tSession = Postmonger.Session = function () {\n\t\tvar args = (arguments.length>0) ? Array.prototype.slice.call(arguments, 0) : [{}];\n\t\tvar connections = [];\n\t\tvar incoming = new Events();\n\t\tvar outgoing = new Events();\n\t\tvar self = this;\n\t\tvar connection, i, j, l, ln, postMessageListener;\n\n\t\t//Session API hooks\n\t\tself.on = incoming.on;\n\t\tself.off = incoming.off;\n\t\tself.trigger = outgoing.trigger;\n\t\tself.end = function () {\n\t\t\tincoming.off();\n\t\t\toutgoing.off();\n\t\t\tif (_window.removeEventListener) {\n\t\t\t\t_window.removeEventListener('message', postMessageListener, false);\n\t\t\t} else if (_window.detachEvent) {\n\t\t\t\t_window.detachEvent('onmessage', postMessageListener);\n\t\t\t}\n\t\t\treturn self;\n\t\t};\n\n\t\t//Establishing connections\n\t\tfor (i=0, l=args.length; i<l; i++) {\n\t\t\tconnection = new Connection(args[i]);\n\t\t\tif (connection) {\n\t\t\t\tfor (j=0, ln=connections.length; j<ln; j++) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tconnections[j].connect === connection.connect &&\n\t\t\t\t\t\tconnections[j].from === connection.from &&\n\t\t\t\t\t\tconnections[j].to === connection.to\n\t\t\t\t\t) {\n\t\t\t\t\t\tconnection = null;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (connection) {\n\t\t\t\t\tconnections.push(connection);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t//Listener for incoming messages\n\t\tpostMessageListener = function(event){\n\t\t\tvar conn = null;\n\t\t\tvar message = [];\n\t\t\tvar data;\n\t\t\tvar k, len;\n\n\t\t\t//Attempt to find the connection we're dealing with\n\t\t\tfor (k=0, len=connections.length; k<len; k++) {\n\t\t\t\tif (connections[k].connect === event.source) {\n\t\t\t\t\tconn = connections[k];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//Check if we've found the connection\n\t\t\tif (!conn) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t//Check if the message is from the expected origin\n\t\t\tif (conn.from !== '*' && conn.from !== event.origin) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t//Check the data that's been passed\n\t\t\ttry{\n\t\t\t\tdata = JSON.parse(event.data);\n\t\t\t\tif(!data.e){\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}catch(e){\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t//Format the passed in data\n\t\t\tmessage.push(data.e);\n\t\t\tdelete data.e;\n\t\t\tfor (k in data) {\n\t\t\t\tmessage.push(data[k]);\n\t\t\t}\n\n\t\t\t//Send the message\n\t\t\tincoming['trigger'].apply(root, message);\n\t\t};\n\n\t\t//Add the listener\n\t\tif (_window.addEventListener) {\n\t\t\t_window.addEventListener('message', postMessageListener, false);\n\t\t} else if(_window.attachEvent) {\n\t\t\t_window.attachEvent('onmessage', postMessageListener);\n\t\t} else{\n\t\t\tif (_window.console && _window.console.warn) {\n\t\t\t\t_window.console.warn('WARNING: Postmonger could not listen for messages on window %o', _window);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t//Sending outgoing messages\n\t\toutgoing.on('all', function () {\n\t\t\tvar args = Array.prototype.slice.call(arguments, 0);\n\t\t\tvar message = {};\n\t\t\tvar k, len;\n\n\t\t\tmessage.e = args[0];\n\n\t\t\tfor (k=1, len=args.length; k<len; k++) {\n\t\t\t\tmessage['a' + k] = args[k];\n\t\t\t}\n\n\t\t\tfor (k=0, len=connections.length; k<len; k++) {\n\t\t\t\tconnections[k].connect.postMessage(JSON.stringify(message), connections[k].to);\n\t\t\t}\n\t\t});\n\n\t\treturn self;\n\t};\n\n\treturn Postmonger;\n}));","output":"str","x":480,"y":580,"wires":[["268afbe1.895354"]]},{"id":"3eb29099.23cbb","type":"http in","z":"f013e682.3118f8","name":"/marketing-cloud","url":"/marketing-cloud","method":"get","upload":false,"swaggerDoc":"","x":120,"y":460,"wires":[["e6a1e098.42916"]]},{"id":"e62252d8.6eaae","type":"catch","z":"f013e682.3118f8","name":"Catch JWT Error","scope":["32b72ce.89070d4"],"uncaught":false,"x":320,"y":820,"wires":[["de462a32.264b48"]]}]