Example REST Activity
config.json
{
"workflowApiVersion": "1.1",
"metaData": {
"icon": "images/sms.png",
"category": "message"
},
"type": "REST",
"lang": {
"en-US": {
"name": "REST Activity (Workflow API v1.1)",
"description": "An example REST activity using workflow API v1.1 format."
}
},
"arguments": {
"execute": {
"inArguments": [
{
"emailAddress": "{{InteractionDefaults.Email}}"
},
{
"phoneNumber": "{{Contact.Default.PhoneNumber}}"
}
],
"outArguments": [
{
"foundSignupDate": ""
}
],
"url": "https://some-endpoint.com/execute"
}
},
"configurationArguments": {
"save": {
"url": "URI/for/your/activity/save"
},
"publish": {
"url": "URI/for/your/activity/publish"
},
"validate": {
"url": "URI/for/your/activity/validate"
},
"stop": {
"url": "URI/for/your/activity/stop"
}
},
"wizardSteps": [
{ "label": "Step 1", "key": "step1" },
{ "label": "Step 2", "key": "step2" },
{ "label": "Step 3", "key": "step3" },
{ "label": "Step 4", "key": "step4", "active": false }
],
"userInterfaces": {
"configModal": {
"height": 200,
"width": 300,
"fullscreen": true
}
},
"schema": {
"arguments": {
"execute": {
"inArguments": [
{
"phoneNumber": {
"dataType": "Phone",
"isNullable": false,
"direction": "in"
}
},
{
"emailAddress": {
"dataType": "Email",
"isNullable": false,
"direction": "in"
}
}
],
"outArguments": [
{
"foundSignupDate": {
"dataType": "Date",
"direction": "out",
"access": "visible"
}
}
]
}
}
}
}index.html
Every custom activity must have an index.html in the root of its endpoint.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Custom Journey Builder Activity</title>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/require.js"></script>
<script type="text/javascript">
(function() {
var config = {
baseUrl: ''
};
var dependencies = [
'customActivity'
];
require(config, dependencies);
})();
</script>
<!--Styles-->
<style type="text/css">
body {
padding: 20px;
margin: 0;
}
.step {
display: none;
}
#step1 {
display: block;
}
</style>
</head>
<body>
<div id="step1" class="step">
Select your SMS message:
<select id="select1">
<option value="">Select an option</option>
<option value="someMessage">Some SMS</option>
<option value="anotherMessage">Another SMS</option>
<option value="thirdMessage">Third SMS</option>
<option value="unknownMessage">Unknown SMS</option>
</select>
<button id="toggleLastStep">Toggle Last Step</button>
</div>
<div id="step2" class="step">
Here's a second page with some info on it.
</div>
<div id="step3" class="step">
You chose the message: <div id="message"></div>
</div>
<div id="step4" class="step">
Hey, here's a fourth step, just for you. Toggle this on or off from step 1.
</div>
</body>
</html>customActivity.js
Postmonger is required for communication between Journey Builder and the activity.
define([
'postmonger'
], function(
Postmonger
) {
'use strict';
var connection = new Postmonger.Session();
var payload = {};
var lastStepEnabled = false;
var steps = [ // initialize to the same value as what's set in config.json for consistency
{ "label": "Step 1", "key": "step1" },
{ "label": "Step 2", "key": "step2" },
{ "label": "Step 3", "key": "step3" },
{ "label": "Step 4", "key": "step4", "active": false }
];
var currentStep = steps[0].key;
$(window).ready(onRender);
connection.on('initActivity', initialize);
connection.on('requestedTokens', onGetTokens);
connection.on('requestedEndpoints', onGetEndpoints);
connection.on('clickedNext', onClickedNext);
connection.on('clickedBack', onClickedBack);
connection.on('gotoStep', onGotoStep);
function onRender() {
// JB will respond the first time 'ready' is called with 'initActivity'
connection.trigger('ready');
connection.trigger('requestTokens');
connection.trigger('requestEndpoints');
// Disable the next button if a value isn't selected
$('#select1').change(function() {
var message = getMessage();
connection.trigger('updateButton', { button: 'next', enabled: Boolean(message) });
$('#message').html(message);
});
// Toggle step 4 active/inactive
// If inactive, wizard hides it and skips over it during navigation
$('#toggleLastStep').click(function() {
lastStepEnabled = !lastStepEnabled; // toggle status
steps[3].active = !steps[3].active; // toggle active
connection.trigger('updateSteps', steps);
});
}
function initialize (data) {
if (data) {
payload = data;
}
var message;
var hasInArguments = Boolean(
payload['arguments'] &&
payload['arguments'].execute &&
payload['arguments'].execute.inArguments &&
payload['arguments'].execute.inArguments.length > 0
);
var inArguments = hasInArguments ? payload['arguments'].execute.inArguments : {};
$.each(inArguments, function(index, inArgument) {
$.each(inArgument, function(key, val) {
if (key === 'message') {
message = val;
}
});
});
// If there is no message selected, disable the next button
if (!message) {
showStep(null, 1);
connection.trigger('updateButton', { button: 'next', enabled: false });
// If there is a message, skip to the summary step
} else {
$('#select1').find('option[value='+ message +']').attr('selected', 'selected');
$('#message').html(message);
showStep(null, 3);
}
}
function onGetTokens (tokens) {
// Response: tokens = { token: <legacy token>, fuel2token: <fuel api token> }
// console.log(tokens);
}
function onGetEndpoints (endpoints) {
// Response: endpoints = { restHost: <url> } i.e. "rest.s1.qa1.exacttarget.com"
// console.log(endpoints);
}
function onClickedNext () {
if (
(currentStep.key === 'step3' && steps[3].active === false) ||
currentStep.key === 'step4'
) {
save();
} else {
connection.trigger('nextStep');
}
}
function onClickedBack () {
connection.trigger('prevStep');
}
function onGotoStep (step) {
showStep(step);
connection.trigger('ready');
}
function showStep(step, stepIndex) {
if (stepIndex && !step) {
step = steps[stepIndex-1];
}
currentStep = step;
$('.step').hide();
switch(currentStep.key) {
case 'step1':
$('#step1').show();
connection.trigger('updateButton', {
button: 'next',
enabled: Boolean(getMessage())
});
connection.trigger('updateButton', {
button: 'back',
visible: false
});
break;
case 'step2':
$('#step2').show();
connection.trigger('updateButton', {
button: 'back',
visible: true
});
connection.trigger('updateButton', {
button: 'next',
text: 'next',
visible: true
});
break;
case 'step3':
$('#step3').show();
connection.trigger('updateButton', {
button: 'back',
visible: true
});
if (lastStepEnabled) {
connection.trigger('updateButton', {
button: 'next',
text: 'next',
visible: true
});
} else {
connection.trigger('updateButton', {
button: 'next',
text: 'done',
visible: true
});
}
break;
case 'step4':
$('#step4').show();
break;
}
}
function save() {
var name = $('#select1').find('option:selected').html();
var value = getMessage();
// 'payload' is initialized on 'initActivity' above.
// Journey Builder sends an initial payload with defaults
// set by this activity's config.json file. Any property
// may be overridden as desired.
payload.name = name;
payload['arguments'].execute.inArguments = [{ "message": value }];
payload['metaData'].isConfigured = true;
connection.trigger('updateActivity', payload);
}
function getMessage() {
return $('#select1').find('option:selected').attr('value').trim();
}
});Sample Request
Sample POST data sent to https://some-endpoint.com/execute.
{
"inArguments": [
{
"emailAddress": "{{InteractionDefaults.Email}}"
},
{
"phoneNumber": "{{Contact.Default.PhoneNumber}}"
}
],
"outArguments": [],
"activityObjectID": "1234abcd-56ef-78gh-90ij-9876klmn5432",
"journeyId": "1234abcd-56ef-78gh-90ij-9876klmn5432",
"activityId": "1234abcd-56ef-78gh-90ij-9876klmn5432",
"definitionInstanceId": "1234abcd-56ef-78gh-90ij-9876klmn5432",
"activityInstanceId": "1234abcd-56ef-78gh-90ij-9876klmn5432",
"keyValue": "someContactKeyHere",
"mode": 0
}Sample Response
Sample 200 OK response expected from the server.
{
"foundSignupDate": "2016-03-10"
}