I am trying to set up a Journey where the DE is populated when someone replies to a SMS with specific keyword. I want the journey to be triggered each time someone is added to the data extension. I'm not sure how I would set up the entry source for this. Any ideas? Thank you!
-
The journey will be already running, perhaps what you want is to have the contact enter the journey as soon as it is added to the DE which is in the entry criteria of the journey. Please have a look at the recurring entry criteria here with processing only new records option help.salesforce.com/…– manjit5190Jul 28, 2020 at 14:50
-
Hey @manjit5190, I understand it is already running but isn't the 3 options just run once, schedule and automation? I don't want it to be running on any schedule as I want them to enter as soon as the data extension is updated - which is whenever the contact responds to the SMS.– s3002068Jul 28, 2020 at 14:53
-
there is not a feature of any JB entry source, you can evaluate using an API entry, a smart capture, of a salesforce entry event to inject when an action occurs– EazyEJul 28, 2020 at 17:38
1 Answer
I have build exactly that scenario and I am happy to share how it has been accomplished.
Prerequisites
- A journey as an API entry event. Copy the API key
- An SMS response message expecting the reply
- SFMC API server-to-server implementation
Flow
- Sending out the SMS and waiting for a response
- Retrieve a SMS response and validate the keyword
- If keyword match, fire the journey entry via API
SMS response setup
In your SMS response message you would need to look for the keyword/reply and fire of the journey. Let's assume we are having two possible replies such as YES
or NO
. When YES
is replied, we want to fire the journey which will create a CRM case.
Notice:
SMS response messages allow the usage of AMP and SSJS. For the ease of maintaining your code, it is a good idea to use ContentBlocks to manage the code and load the code inside the SMS response message. If you are doing so, keep in mind that the SMS response message will fail if there is any form of output outside the message itself. Output() or Write() inside the ContentBlock will fail the message
Code for the SMS response message:
%%[
/* The valid replies */
SET @validReplies = 'YES|NO'
/* the keyword you used for the response */
SET @keyword = 'MYKEYWORD'
/* Load the content */
ContentBlockByKey('ContentBlockExternalKey')
]%%
%%=v(@msg)=%%
Code of the ContentBlock for the SMS response:
%%[
/* Fetch the user reply */
SET @reply = [MSG(0).NOUN(0)]
/* Check if reply is allowed (set in the message itself) */
IF IndexOf(@validReplies,@reply) > 0 THEN
/* ======================== REPLY ======================== */
IF @reply == 'YES' THEN
/* Load the journey fire event from an external content block */
Output(ContentBlockByKey('ExternalKeyForYourJourneyFireCode'))
SET @msg = 'Thank you, a customer service agent will contact you shortly.'
ELSEIF @reply == 'NO' THEN
SET @msg = 'Thank you!'
ENDIF
/* The given reply is not allowed */
ELSE
/* Build the response such as "Please reply only with MYKEYWORD YES or MYKEYWORD NO" */
SET @validReply = BuildRowsetFromString(@validReplies,'|')
SET @msg = 'Please reply only with '
FOR @i = 1 to RowCount(@validReply) DO
SET @msg = Concat(@msg, UpperCase(@keyword),' ',Field(Row(@validReply, @i),1))
IF @i < RowCount(@validReply) THEN
SET @msg = Concat(@msg, ' or ')
ELSE
SET @msg = Concat(@msg, '.')
ENDIF
NEXT @i
ENDIF
]%%
Code for the Journey ContentBlock:
<script runat=server>
Platform.Load("Core", "1.1.1");
// API credentials
var setup = {
authBaseURI : "https://xxxxxxxxxxxxxxxxxx.auth.marketingcloudapis.com/",
restBaseURI : "https://xxxxxxxxxxxxxxxxxx.rest.marketingcloudapis.com/",
soapBaseURI : "https://xxxxxxxxxxxxxxxxxx.soap.marketingcloudapis.com/",
clientId : "xxxxxxxxxxxxxxxxxx",
clientSecret : "xxxxxxxxxxxxxxxxxx"
};
// YOUR API EVENT KEY
var apiKey = 'APIEvent-xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx';
// DATA YOU WANT TO PASS TO THE JOURNEY
var data = {
'Reply ': Variable.GetValue("@reply"),
'SubscriberKey': Variable.GetValue("@SubscriberKey")
};
// TRIGGER THE JOURNEY
triggerJourneyEvent(data.SubscriberKey,apiKey,data);
// *********************** FUNCTIONS *********************** //
// FUNCTION TO GET A TOKEN
function getToken() {
// set data for the API call
var config = {
url : setup.authBaseURI + "v2/token",
contentType : "application/json",
payload : {
"client_id": setup.clientId,
"client_secret": setup.clientSecret,
"grant_type": "client_credentials"
}
};
// generate new token
var req = httpRequest('POST', config.url, config.contentType, config.payload);
if (req.status == 200) {
return token;
} else {
return null;
}
}
// FIRE OF AN HTTP REQUEST
function httpRequest(method,url,contentType,payload,header) {
var req = new Script.Util.HttpRequest(url);
req.emptyContentHandling = 0;
req.retries = 2;
req.continueOnError = true;
req.method = method;
for( var k in header ) {
req.setHeader(k, header[k]);
}
if(typeof contentType !== 'undefined' && contentType !== null) { req.contentType = contentType; }
if(typeof payload !== 'undefined' && payload !== null) { req.postData = Stringify(payload); }
try {
var res = req.send();
return {
status: Platform.Function.ParseJSON(String(res.statusCode)),
content: Platform.Function.ParseJSON(String(res.content))
};
} catch(e) {
return {
status: '500',
content: e
};
}
}
// FIRE THE JOURNEY
function triggerJourneyEvent(contactKey,eventDefinitionKey,data) {
var token = getToken(),
config = {
url: setup.restBaseURI + "interaction/v1/events",
contentType: "application/json",
header: {
Authorization: "Bearer " + token
},
payload : {
ContactKey: contactKey,
EventDefinitionKey: eventDefinitionKey,
Data: data
}
};
var req = httpRequest('POST',config.url, config.contentType, config.payload, config.header);
if (req.status == 201) {
if (req.content.eventInstanceId != null && req.content.eventInstanceId != "") {
return true;
} else {
return false;
}
} else {
return false;
}
}
</script>
This will allow you to fire of a Journey form a SMS response.
Keep in mind, the best practice is to re-use the API token for its duration and not generate a new one every time. I have achieved that by saving the token in a DataExtension but removed it from this example.
These code snippets are part of my library I have written which also has the token logic to save it in a DataExtension. You can find the library here.