Example Contact Event

config.json 

1{
2  "workflowApiVersion": "1.1",
3  "metaData": {
4    "icon": "images/icon.png",
5    "transactionKeys": {
6      "0": {
7        "from": "Event Property",
8        "to": "DE_Name.DE_Property"
9      }
10    }
11  },
12  "type": "Event",
13  "lang": {
14    "en-US": {
15      "name": "Event (Workflow API v1.1)",
16      "description": "An example event using workflow API v1.1 format.",
17      "selectCountry": "Select Country",
18      "enterFirstName": "Enter First Name",
19      "enterLastName": "Enter Last Name",
20      "enterFavoriteFood": "Enter Favorite Food",
21      "countryCodeLabel": "Country Code",
22      "firstNameLabel": "First Name",
23      "lastNameLabel": "Last Name",
24      "favoriteFoodLabel": "Favorite Food",
25      "selectTransactionKeyMapping": "Transaction Key"
26    }
27  },
28  "configurationArguments": {},
29  "filterExpressionEnabled": false,
30  "wizardSteps": [
31    { "key": "selectCountry", "label": "selectCountry" },
32    { "key": "enterFirstName", "label": "enterFirstName" },
33    { "key": "enterLastName", "label": "enterLastName" },
34    {
35      "key": "enterFavoriteFood",
36      "label": "enterFavoriteFood",
37      "active": false
38    },
39    {
40      "key": "selectTransactionKeyMapping",
41      "label": "selectTransactionKeyMapping"
42    }
43  ],
44  "userInterfaces": {
45    "configModal": {
46      "url": "index.html"
47    },
48    "summary": [
49      {
50        "valuePath": "arguments.countryCode",
51        "label": "countryCodeLabel"
52      },
53      {
54        "valuePath": "arguments.firstName",
55        "label": "firstNameLabel"
56      },
57      {
58        "valuePath": "arguments.lastName",
59        "label": "lastNameLabel"
60      },
61      {
62        "valuePath": "arguments.favoriteFood",
63        "label": "favoriteFoodLabel"
64      },
65      {
66        "valuePath": "metaData.transactionKeys",
67        "label": "Transaction Key"
68      }
69    ]
70  }
71}

index.html 

Every custom event 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 Event Test</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 = ["customEvent"];
17
18        require(config, dependencies);
19      })();
20    </script>
21
22    <!--Styles-->
23    <style type="text/css">
24      body {
25        padding: 15px;
26        margin: 0;
27      }
28      h1 {
29        font-size: 16px;
30      }
31      .step {
32        display: none;
33      }
34      #step1 {
35        display: block;
36      }
37    </style>
38  </head>
39  <body>
40    <div id="step1" class="step">
41      <h1>Step 1: Country Code</h1>
42      <select id="select-country-code">
43        <option value="US">United States</option>
44        <option value="GB">United Kingdom</option>
45        <option value="BR">Brazil</option>
46        <option value="DE">Germany</option>
47      </select>
48      <br />
49      <button id="toggleLastStep">Toggle Favorite Food Step</button>
50    </div>
51    <div id="step2" class="step">
52      <h1>Step 2: First Name</h1>
53      <input id="select-first-name" />
54    </div>
55    <div id="step3" class="step">
56      <h1>Step 3: Last Name</h1>
57      <input id="select-last-name" />
58    </div>
59    <div id="step4" class="step">
60      <h1>Step 4: Favorite Food</h1>
61      <input id="select-favorite-food" />
62    </div>
63  </body>
64</html>

customEvent.js 

Postmonger is required for communication between Journey Builder and the custom event.

1define(["js/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    { key: "selectCountry", label: "selectCountry" },
10    { key: "enterFirstName", label: "enterFirstName" },
11    { key: "enterLastName", label: "enterLastName" },
12    { key: "enterFavoriteFood", label: "enterFavoriteFood", active: false },
13  ];
14  var currentStep = steps[0].key;
15
16  $(window).ready(onRender);
17
18  connection.on("initEvent", 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 initialize(data) {
27    var countryCode;
28    var firstName;
29    var lastName;
30
31    if (data) {
32      payload = data;
33    }
34
35    if (payload["arguments"]) {
36      countryCode = payload["arguments"].countryCode;
37      firstName = payload["arguments"].firstName;
38      lastName = payload["arguments"].lastName;
39    }
40
41    $("#select-country-code").val(countryCode);
42    $("#select-first-name").val(firstName);
43    $("#select-last-name").val(lastName);
44  }
45
46  function onGetTokens(tokens) {
47    // Response: tokens = { token: <legacy token>, fuel2token: <fuel api token> }
48    // console.log(tokens);
49  }
50
51  function onGetEndpoints(endpoints) {
52    // Response: endpoints = { restHost: <url> } i.e. "rest.s1.qa1.exacttarget.com"
53    // console.log(endpoints);
54  }
55
56  function onClickedNext() {
57    if (
58      (currentStep.key === "enterLastName" && steps[3].active === false) ||
59      currentStep.key === "enterFavoriteFood"
60    ) {
61      save();
62    } else {
63      connection.trigger("nextStep");
64    }
65  }
66
67  function onClickedBack() {
68    connection.trigger("prevStep");
69  }
70
71  function onGotoStep(step) {
72    showStep(step);
73    connection.trigger("ready");
74  }
75
76  function onRender() {
77    connection.trigger("ready"); // JB will respond the first time 'ready' is called with 'initEvent'
78
79    connection.trigger("requestTokens");
80    connection.trigger("requestEndpoints");
81
82    $("#toggleLastStep").click(function () {
83      lastStepEnabled = !lastStepEnabled; // toggle status
84      steps[3].active = !steps[3].active; // toggle active
85
86      connection.trigger("updateSteps", steps);
87    });
88  }
89
90  function showStep(step, stepIndex) {
91    if (stepIndex && !step) {
92      step = steps[stepIndex - 1];
93    }
94
95    currentStep = step;
96
97    $(".step").hide();
98
99    switch (currentStep.key) {
100      case "selectCountry":
101        $("#step1").show();
102        break;
103      case "enterFirstName":
104        $("#step2").show();
105        $("#step2 input").focus();
106        break;
107      case "enterLastName":
108        $("#step3").show();
109        $("#step3 input").focus();
110        break;
111      case "enterFavoriteFood":
112        $("#step4").show();
113        $("#step4 input").focus();
114        break;
115    }
116  }
117
118  function save() {
119    var countryCode = $("#select-country-code")
120      .find("option:selected")
121      .attr("value");
122    var firstName = $("#select-first-name").val();
123    var lastName = $("#select-last-name").val();
124    var favoriteFood = $("#select-favorite-food").val();
125
126    payload["arguments"] = payload["arguments"] || {};
127    payload["arguments"].countryCode = countryCode;
128    payload["arguments"].firstName = firstName;
129    payload["arguments"].lastName = lastName;
130
131    // Example criteria - if 'filterExpressionEnabled' is set to true in config.json, Journey Builder will
132    // populate this step with the 'criteria' XML passed here
133    // payload['arguments'].criteria = "<FilterDefinition Source='SubscriberAttribute'><ConditionSet Operator='AND' ConditionSetName='Grouping'><Condition ID='13D65BB5-1F98-E411-9D68-00237D5401CE' isParam='false' Operator='Equal' operatorEditable='0' valueEditable='1' annotation=''><Value><![CDATA[" + countryCode + "]]></Value></Condition><Condition ID='0CD65BB5-1F98-E411-9D68-00237D5401CE' isParam='false' Operator='Equal' operatorEditable='0' valueEditable='1' annotation=''><Value><![CDATA[" + firstName + "]]></Value></Condition><Condition ID='12D65BB5-1F98-E411-9D68-00237D5401CE' isParam='false' Operator='Equal' operatorEditable='0' valueEditable='1' annotation=''><Value><![CDATA[" + lastName + "]]></Value></Condition></ConditionSet></FilterDefinition>";
134
135    if (favoriteFood && steps[3].active) {
136      payload["arguments"].favoriteFood = favoriteFood;
137    }
138
139    payload["metaData"] = payload["metaData"] || {};
140
141    payload["configurationArguments"] = payload["configurationArguments"] || {};
142
143    payload.dataExtensionId = "<data extension ID>";
144
145    connection.trigger("updateEvent", payload);
146  }
147});