Two-step data integration
Personally identifiable information (PII) is often required by Rokt to fulfill campaign goals. For example, a customer's email address is considered PII and is essential for Rokt to deliver an email campaign. These pieces of information are known as fulfillment attributes. Rokt has designed a two-step data integration process so that you can securely integrate fulfillment attributes to Rokt, ensuring privacy for the customer and maintaining the quality of their experience.
With the two-step data integration process, Rokt uses hashed email address to identify the customer and choose the most relevant experience for them. If the customer positively engages with the Rokt offer, you disclose the attributes needed to fulfill the offer. The two-step data integration process gives an added measure of security to customers while allowing Rokt to deliver on some of the expected functionalities (e.g., suppressing offers for returning customers).
The two-step data integration can be implemented across the Web, iOS, Android, and React Native SDKs. The following code shows the basic integration process in various languages:
- JavaScript
- Swift
- Objective-C
- Kotlin
- Java
- React
const rawAttributes = {
country: "US",
};
const twoStepDataIntegrationAttributes = {
email: "john.smith@rokt.com",
};
const hashedAttributes = await launcher.hashAttributes(
twoStepDataIntegrationAttributes
);
const selection = await launcher.selectPlacements({
attributes: {
...rawAttributes,
...hashedAttributes,
},
});
selection.on("POSITIVE_ENGAGEMENT").subscribe(() => {
selection.setAttributes(twoStepDataIntegrationAttributes);
});
The above code uses the IntegrationLauncher.hashAttributes to hash fulfillment attributes. Rokt recommends the use of this utility method in case additional attributes need to be hashed as well one day or more complex hashing algorithms need to be seemlessly supported.
Should you choose to hash the attributes please ensure that the attribute value is lowercased and trimmed of whitespace prior to hashing and attribute name is postfix with sha256
The below example shows the use of pre-hashed attribute.
const email = "john.smith@rokt.com ";
const emailsha256 =
"F6191D8D6A0F75CA7D237ED07CF8E461E72E5C7BB28D611DDF37E5908FDB632B";
const selection = await launcher.selectPlacements({
attributes: {
country: "US",
emailsha256: emailsha256,
},
});
selection.on("POSITIVE_ENGAGEMENT").subscribe(() => {
selection.setAttributes({
email: email,
});
});
import Rokt_Widget
class OrderConfirmationViewController: UIViewController {
...
// call this function when the placement needs to be shown
func showWidget() {
let attributes = ["firstname": "Jenny",
"lastname": "Smith",
"emailsha256": "36f15652992f5c291ea233b930c8c435ee5a5fb115d25933e3266591d3f92f74",
"postcode": "90210",
"country": "US"]
Rokt.execute2step(viewName: "RoktExperience", attributes: attributes, onLoad: {
// Optional callback for when the Rokt placement loads
}, onUnLoad: {
// Optional callback for when the Rokt placement unloads
}, onShouldShowLoadingIndicator: {
// Optional callback to show a loading indicator
}, onShouldHideLoadingIndicator: {
// Optional callback to hide a loading indicator
}, onEvent: { eventType , eventHandler in
if eventType == .FirstPositiveEngagement {
eventHandler.setFulfillmentAttributes(attributes: ["email": "j.smith@example.com"])
}
}
)
}
}
#import <Rokt_Widget/Rokt_Widget-Swift.h>
...
// call this function when placement needs to be shown
- (void)showWidget {
NSDictionary *attributes = @{
@"firstname": @"Jenny",
@"lastname": @"Smith",
@"emailsha256": @"36f15652992f5c291ea233b930c8c435ee5a5fb115d25933e3266591d3f92f74",
@"postcode": @"90210",
@"country": @"US"
};
[Rokt execute2stepWithViewName: @"RoktExperience"
attributes:attributes
placements:placements
onLoad:^{
// Optional callback for when the Rokt widget loads
} onUnLoad:^{
// Optional callback for when the Rokt widget unloads
} onShouldShowLoadingIndicator:^{
// Optional callback to show a loading indicator
} onShouldHideLoadingIndicator:^{
// Optional callback to hide a loading indicator
} onEmbeddedSizeChange:^(NSString *selectedPlacement, CGFloat widgetHeight){
// Optional callback to get selectedPlacement and height required by widget every time the height of widget changes
} onEvent:^(RoktEventType eventType, RoktEventHandler* eventHandler){
if(eventType == RoktEventTypeFirstPositiveEngagement){
[eventHandler setFulfillmentAttributesWithAttributes: @{
@"email" : @"j.smith@example.com"}];
}
}];
}
import com.rokt.roktsdk.Widget
import com.rokt.roktsdk.Rokt
class ConfirmationActivity : Activity() {
private val roktCalllback = object : Rokt.RoktCallback {
override fun onLoad() {
}
override fun onShouldShowLoadingIndicator() {
}
override fun onShouldHideLoadingIndicator() {
}
override fun onUnload(reason: Rokt.UnloadReasons) {
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
// Include any appropriate consumer attributes
val attributes = hashMapOf(
Pair("emailsha256", "36f15652992f5c291ea233b930c8c435ee5a5fb115d25933e3266591d3f92f74"),
Pair("firstname", "Jenny"),
Pair("lastname", "Smith"),
Pair("mobile", "(323) 867-5309"),
Pair("postcode", "90210"),
Pair("country", "US")
)
// Widget placeholder mapping the placeholder view with placement location configuration
val widget = findViewById<Widget>(R.id.roktWidget)
val placeHolders = hashMapOf(
Pair("RoktEmbedded1", WeakReference(widget))
)
Rokt.execute2Step(pageIdentifier, attributes, this, placeholders, object : Rokt.RoktEventCallback {
override fun onEvent(eventType: Rokt.RoktEventType, roktEventHandler: Rokt.RoktEventHandler) {
if (eventType == Rokt.RoktEventType.FirstPositiveEngagement) {
roktEventHandler.setFulfillmentAttributes(mapOf("email" to "j.smith@example.com"))
}
}
})
...
}
}
import com.rokt.roktsdk.Widget;
import com.rokt.roktsdk.Rokt;
class ConfirmationActivity : Activity() {
private Rokt.RoktCallback roktCallback = new Rokt.RoktCallback() {
@Override
public void onLoad() {
}
@Override
public void onShouldShowLoadingIndicator() {
}
@Override
public void onShouldHideLoadingIndicator() {
}
@Override
public void onUnload(@NotNull Rokt.UnloadReasons unloadReasons) {
}
};
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
Map<String, String> attributes = new HashMap<String, String>();
attributes.put("emailsha256", "36f15652992f5c291ea233b930c8c435ee5a5fb115d25933e3266591d3f92f74");
attributes.put("firstname", "Jenny");
attributes.put("lastname", "Smith");
attributes.put("mobile", "(323) 867-5309");
attributes.put("postcode", "90210");
attributes.put("country", "US");
// Widget placeholder mapping the placeholder view with placement location configuration
Widget widget = findViewById(R.id.roktWidget);
Map<String, WeakReference<Widget>> placeHolders = new HashMap<>();
placeHolders.put("RoktEmbedded1", new WeakReference<>(widget));
Rokt.INSTANCE.execute2Step("test,", roktCallback, new Rokt.RoktEventCallback() {
@Override
public void onEvent(Rokt.RoktEventType roktEventType, Rokt.RoktEventHandler roktEventHandler) {
Map<String,String> attributes = new HashMap<String, String>();
attributes.put("email", "j.smith@example.com");
if (roktEventType == Rokt.RoktEventType.FirstPositiveEngagement){
roktEventHandler.setFulfillmentAttributes(attributes);
}
}
});
...
}
}
import {
Rokt,
RoktEventManager
}
from "@rokt/react-native-sdk";
const roktEventManager = new NativeEventEmitter(RoktEventManager);
//Subscribe to FirstPositiveResponse event.
subscription = roktEventManager.addListener(
'FirstPositiveResponse',
(x) => {
console.log("Widget OnFirstPositiveEvent Callback");
// Send unhashed email on first positive response
fullfilmentAttributes = {
email: "j.smith@example.com"
};
Rokt.setFulfillmentAttributes(fullfilmentAttributes);
}
);
onExecuteHandler = () => {
attributes = {
"emailsha256": "238afb9e862add7d56bbc4181b80fa32",
"firstname": "Jenny",
"lastname": "Smith",
"mobile": "(323) 867-5309",
"postcode": "90210",
"country": "US"
};
Rokt.execute2Step("RoktExperience", attributes, null, () => console.log("Placement Loaded"));
}
// Remove subscription
componentWillUnmount() {
this.subscription.remove();
}