Creating time sensitive offers with Adobe Target


Adding urgency to your campaigns can really move-the-needle, if you need convincing - try reading up on the scarcity principle. In this article I'll show you how to test and report on time sensitive offers using Adobe Target. The below example will show you how to create URGENT offers that expire within 30-90 minutes (this can be adjusted). So without further ado lets get to it!

Creating the 1st Profile Attribute

I've named this Profile Attribute; getOfferExpiryHour and here's what the below code is doing:
  • Checking to see if the visitor has seen the offer before, if so are they in the allowed time-frame? If not they're set to expired and they've missed the promotion, otherwise we let them through. 
  • The else clause is doing most of the work - here we have a couple of functions that convert the time from 24hr format (probably nicer to use when outputting in our offer) and another that returns am or pm - again used for copy purposes in our offer.
  • The rest is related to time - we're using the visitor's time which is based on their system settings. The visitor will have between 30-90 minutes to complete this offer depending on what time they arrive.

// only run this script with the below mbox
if (mbox.name == 'YourMboxName') 
{
 // has visitor already seen this offer
 var offerExpiryHour = user.getLocal('offerExpiryHour');
 
 // returning visitor that has seen this offer
 if(offerExpiryHour)
 {
  if (profile.browserTime.getHours() >= user.getLocal('offerExpiryHour24') || 
      profile.browserTime.getDate() != user.getLocal('offerValidDate'))
  {
   return "expired";
  }
  else
  {
   return offerExpiryHour;
  }
 }

  // vistor's first time seeing the offer
 else
 {
  var hour    = null;
  var seconds = null; 

  // is it AM or PM? - used when outputting copy
  function amPmDecider(hour)
  {
   if (hour == 0 || hour == 1 || hour == 2 || hour == 3 || hour == 4 || 
       hour == 5 || hour == 6 || hour == 7 || hour == 8 || hour == 9 || 
       hour == 10 || hour == 11)
   {
    return "am";
   }
   else
   {
    return "pm";
   }
  }

  // convert to non-24hr format 
  function convert24Hr(hour)
  {
   switch(hour)
   {
   case 13:
    return 1;
 
   case 14:
    return 2;
 
   case 15:
    return 3;
 
   case 16:
    return 4;
 
   case 17:
    return 5;
 
   case 18:
    return 6;
 
   case 19:
    return 7;
 
   case 20:
    return 8;
 
   case 21:
    return 9;
 
   case 22:
    return 10;
 
   case 23:
    return 11;
 
   case 24:
    return 12;
 
   default:
    return hour;
   }
  }

  // tnt profile variables
  var currentHour   = profile.browserTime.getHours  ();
  var currentMinute = profile.browserTime.getMinutes();
  var currentDate   = profile.browserTime.getDate   ();

  // round up time - if user visits at 11:59 we don't want offer to expire at 12:00
  var d = new Date(1970, 0, 1, currentHour, currentMinute);
  d.setMinutes (d.getMinutes() + 30);
  d.setMinutes (0);
 
  // add 1 hour 
  var offerExpiryHour = d.getHours() + 1;

  // store 24hr format for future comparison 
  user.setLocal('offerExpiryHour24', offerExpiryHour);

  // store date of when user was exposed to offer
  user.setLocal('offerValidDate', currentDate);

  var timeSuffix      = amPmDecider(offerExpiryHour);
  var offerExpiryHour = convert24Hr(offerExpiryHour);

  user.setLocal('offerExpiryHour', offerExpiryHour+timeSuffix);

  return offerExpiryHour+timeSuffix;
 }
}

Creating the 2nd Profile Attribute

The below Profile Attribute has been named getNumBetween1and2 and is used for identifying whether the visitor has seen the time sensitive experience or the control version - we'll need this later for targeting. I've written about what's going on here in more detail in a previous post.

// target script only for this mbox
if (mbox.name == 'YourMboxName') 
{
  // has the visitor already been exposed to this script?
  var numBetween1and2 = user.getLocal('numBetween1and2');

  // yes they have so no need to re-run
  if (numBetween1and2)
  {
    return numBetween1and2;
  }
  // no they haven't so return a number between 1 & 2
  else
  {
    var x = Math.floor((Math.random()*2)+1); 
    user.setLocal('numBetween1and2', x);

    return x;
  }
}


Creating the offer

Now with the above Profile Attributes activated we can create our Offer with added urgency. The getOfferExpiryHour Attribute returns the expiry time, so in our offer we need to reference getOfferExpiryHour which looks something like this:
<h1>Offer Ends ${user.getOfferExpiryHour} today: save 50% on all iPhones!</h1>
<!-- 
This will look something like this:
Offer Ends 5pm today: save 50% on all iPhones!
-->

Creating the campaign

Our campaign type needs to be: Landing Page Campaign if it's not then when the visitor returns after the expired period s/he will see the limited time offer again which isn't good. Targeting rules for Landing Page Campaigns are reevaluated whereas the more common A/B aren't.

In addition we need to create targeting for the returning Control and (expired) Variant visitors, so we can monitor/compare the performance of those that return once the offer has expired. The returning Control experience is just a copy of the default content while the returning Variant experience would be the full-priced / non-promotion offer.

So in total we have 4 experiences:
  • The Control - your default content without the urgent offer.
  • Variant - your urgent offer.
  • Returning control - returning visitors that had previously seen the Control.
  • Returning variant - the offer has expired and these are returning visitors that had previously seen the Variant, we would now show them the full price/ non-offer content.
The targeting looks like this for the Control:



The variant's targeting is the same other than the getNumBetween1and2 profile attribute is checking for 2 instead of 1.

Final reports 

So with your targeting and campaign live you'll end up with reports that look like this:

And that's it! I've seen some significant lifts when adding a sense of genuine urgency to offers and hopefully you will too.