Installed Package Types
Create Custom Activity UI
Register Endpoint for a Custom Component
Example Rest Activity
List Your App in AppExchange
1{
2 "workflowApiVersion": "1.1",
3 "metaData": {
4 "icon": "images/sms.png",
5 "category": "message"
6 },
7 "type": "REST",
8 "lang": {
9 "en-US": {
10 "name": "REST Activity (Workflow API v1.1)",
11 "description": "An example REST activity using workflow API v1.1 format."
12 }
13 },
14 "arguments": {
15 "execute": {
16 "inArguments": [
17 {
18 "emailAddress": "{{InteractionDefaults.Email}}"
19 },
20 {
21 "phoneNumber": "{{Contact.Default.PhoneNumber}}"
22 }
23 ],
24 "outArguments": [
25 {
26 "foundSignupDate": ""
27 }
28 ],
29 "url": "https://some-endpoint.com/execute"
30 }
31 },
32 "configurationArguments": {
33 "save": {
34 "url": "URI/for/your/activity/save"
35 },
36 "publish": {
37 "url": "URI/for/your/activity/publish"
38 },
39 "validate": {
40 "url": "URI/for/your/activity/validate"
41 },
42 "stop": {
43 "url": "URI/for/your/activity/stop"
44 }
45 },
46 "wizardSteps": [
47 { "label": "Step 1", "key": "step1" },
48 { "label": "Step 2", "key": "step2" },
49 { "label": "Step 3", "key": "step3" },
50 { "label": "Step 4", "key": "step4", "active": false }
51 ],
52 "userInterfaces": {
53 "configModal": {
54 "height": 200,
55 "width": 300,
56 "fullscreen": true
57 }
58 },
59 "schema": {
60 "arguments": {
61 "execute": {
62 "inArguments": [
63 {
64 "phoneNumber": {
65 "dataType": "Phone",
66 "isNullable": false,
67 "direction": "in"
68 }
69 },
70 {
71 "emailAddress": {
72 "dataType": "Email",
73 "isNullable": false,
74 "direction": "in"
75 }
76 }
77 ],
78 "outArguments": [
79 {
80 "foundSignupDate": {
81 "dataType": "Date",
82 "direction": "out",
83 "access": "visible"
84 }
85 }
86 ]
87 }
88 }
89 }
90}Every custom activity must have an index.html in the root of its endpoint.
1<!DOCTYPE html>
2
3<html lang="en">
4 <head>
5 <meta charset="utf-8" />
6 <title>Custom Journey Builder Activity</title>
7
8 <script type="text/javascript" src="js/jquery.min.js"></script>
9 <script type="text/javascript" src="js/require.js"></script>
10 <script type="text/javascript">
11 (function () {
12 var config = {
13 baseUrl: "",
14 };
15
16 var dependencies = ["customActivity"];
17
18 require(config, dependencies);
19 })();
20 </script>
21
22 <!--Styles-->
23 <style type="text/css">
24 body {
25 padding: 20px;
26 margin: 0;
27 }
28 .step {
29 display: none;
30 }
31 #step1 {
32 display: block;
33 }
34 </style>
35 </head>
36 <body>
37 <div id="step1" class="step">
38 Select your SMS message:
39 <select id="select1">
40 <option value="">Select an option</option>
41 <option value="someMessage">Some SMS</option>
42 <option value="anotherMessage">Another SMS</option>
43 <option value="thirdMessage">Third SMS</option>
44 <option value="unknownMessage">Unknown SMS</option>
45 </select>
46
47 <button id="toggleLastStep">Toggle Last Step</button>
48 </div>
49 <div id="step2" class="step">
50 Here's a second page with some info on it.
51 </div>
52 <div id="step3" class="step">
53 You chose the message:
54 <div id="message"></div>
55 </div>
56 <div id="step4" class="step">
57 Hey, here's a fourth step, just for you. Toggle this on or off from step
58 1.
59 </div>
60 </body>
61</html>Postmonger is required for communication between Journey Builder and the activity.
1define(["postmonger"], function (Postmonger) {
2 "use strict";
3
4 var connection = new Postmonger.Session();
5 var payload = {};
6 var lastStepEnabled = false;
7 var steps = [
8 // initialize to the same value as what's set in config.json for consistency
9 { label: "Step 1", key: "step1" },
10 { label: "Step 2", key: "step2" },
11 { label: "Step 3", key: "step3" },
12 { label: "Step 4", key: "step4", active: false },
13 ];
14 var currentStep = steps[0].key;
15
16 $(window).ready(onRender);
17
18 connection.on("initActivity", initialize);
19 connection.on("requestedTokens", onGetTokens);
20 connection.on("requestedEndpoints", onGetEndpoints);
21
22 connection.on("clickedNext", onClickedNext);
23 connection.on("clickedBack", onClickedBack);
24 connection.on("gotoStep", onGotoStep);
25
26 function onRender() {
27 // JB will respond the first time 'ready' is called with 'initActivity'
28 connection.trigger("ready");
29
30 connection.trigger("requestTokens");
31 connection.trigger("requestEndpoints");
32
33 // Disable the next button if a value isn't selected
34 $("#select1").change(function () {
35 var message = getMessage();
36 connection.trigger("updateButton", {
37 button: "next",
38 enabled: Boolean(message),
39 });
40
41 $("#message").html(message);
42 });
43
44 // Toggle step 4 active/inactive
45 // If inactive, wizard hides it and skips over it during navigation
46 $("#toggleLastStep").click(function () {
47 lastStepEnabled = !lastStepEnabled; // toggle status
48 steps[3].active = !steps[3].active; // toggle active
49
50 connection.trigger("updateSteps", steps);
51 });
52 }
53
54 function initialize(data) {
55 if (data) {
56 payload = data;
57 }
58
59 var message;
60 var hasInArguments = Boolean(
61 payload["arguments"] &&
62 payload["arguments"].execute &&
63 payload["arguments"].execute.inArguments &&
64 payload["arguments"].execute.inArguments.length > 0
65 );
66
67 var inArguments = hasInArguments
68 ? payload["arguments"].execute.inArguments
69 : {};
70
71 $.each(inArguments, function (index, inArgument) {
72 $.each(inArgument, function (key, val) {
73 if (key === "message") {
74 message = val;
75 }
76 });
77 });
78
79 // If there is no message selected, disable the next button
80 if (!message) {
81 showStep(null, 1);
82 connection.trigger("updateButton", { button: "next", enabled: false });
83 // If there is a message, skip to the summary step
84 } else {
85 $("#select1")
86 .find("option[value=" + message + "]")
87 .attr("selected", "selected");
88 $("#message").html(message);
89 showStep(null, 3);
90 }
91 }
92
93 function onGetTokens(tokens) {
94 // Response: tokens = { token: <legacy token>, fuel2token: <fuel api token> }
95 // console.log(tokens);
96 }
97
98 function onGetEndpoints(endpoints) {
99 // Response: endpoints = { restHost: <url> } i.e. "rest.s1.qa1.exacttarget.com"
100 // console.log(endpoints);
101 }
102
103 function onClickedNext() {
104 if (
105 (currentStep.key === "step3" && steps[3].active === false) ||
106 currentStep.key === "step4"
107 ) {
108 save();
109 } else {
110 connection.trigger("nextStep");
111 }
112 }
113
114 function onClickedBack() {
115 connection.trigger("prevStep");
116 }
117
118 function onGotoStep(step) {
119 showStep(step);
120 connection.trigger("ready");
121 }
122
123 function showStep(step, stepIndex) {
124 if (stepIndex && !step) {
125 step = steps[stepIndex - 1];
126 }
127
128 currentStep = step;
129
130 $(".step").hide();
131
132 switch (currentStep.key) {
133 case "step1":
134 $("#step1").show();
135 connection.trigger("updateButton", {
136 button: "next",
137 enabled: Boolean(getMessage()),
138 });
139 connection.trigger("updateButton", {
140 button: "back",
141 visible: false,
142 });
143 break;
144 case "step2":
145 $("#step2").show();
146 connection.trigger("updateButton", {
147 button: "back",
148 visible: true,
149 });
150 connection.trigger("updateButton", {
151 button: "next",
152 text: "next",
153 visible: true,
154 });
155 break;
156 case "step3":
157 $("#step3").show();
158 connection.trigger("updateButton", {
159 button: "back",
160 visible: true,
161 });
162 if (lastStepEnabled) {
163 connection.trigger("updateButton", {
164 button: "next",
165 text: "next",
166 visible: true,
167 });
168 } else {
169 connection.trigger("updateButton", {
170 button: "next",
171 text: "done",
172 visible: true,
173 });
174 }
175 break;
176 case "step4":
177 $("#step4").show();
178 break;
179 }
180 }
181
182 function save() {
183 var name = $("#select1").find("option:selected").html();
184 var value = getMessage();
185
186 // 'payload' is initialized on 'initActivity' above.
187 // Journey Builder sends an initial payload with defaults
188 // set by this activity's config.json file. Any property
189 // may be overridden as desired.
190 payload.name = name;
191
192 payload["arguments"].execute.inArguments = [{ message: value }];
193
194 payload["metaData"].isConfigured = true;
195
196 connection.trigger("updateActivity", payload);
197 }
198
199 function getMessage() {
200 return $("#select1").find("option:selected").attr("value").trim();
201 }
202});Sample POST data sent to https://some-endpoint.com/execute.
1{
2 "inArguments": [
3 {
4 "emailAddress": "{{InteractionDefaults.Email}}"
5 },
6 {
7 "phoneNumber": "{{Contact.Default.PhoneNumber}}"
8 }
9 ],
10 "outArguments": [],
11 "activityObjectID": "1234abcd-56ef-78gh-90ij-9876klmn5432",
12 "journeyId": "1234abcd-56ef-78gh-90ij-9876klmn5432",
13 "activityId": "1234abcd-56ef-78gh-90ij-9876klmn5432",
14 "definitionInstanceId": "1234abcd-56ef-78gh-90ij-9876klmn5432",
15 "activityInstanceId": "1234abcd-56ef-78gh-90ij-9876klmn5432",
16 "keyValue": "someContactKeyHere",
17 "mode": 0
18}Sample 200 OK response expected from the server.
1{
2 "foundSignupDate": "2016-03-10"
3}