1:1 Coupon Assignment for Blasts
Leverage Simon's Journeys product to allocate coupons 1:1 to users within different variants of a campaign.
This feature leverages our Journeys Node Unique ID functionality. Read through our documentation to understand how it works!
Use Case
The solution outlined below allocates unique coupon codes 1:1 each contact in a segment or variant of a campaign. This is particularly valuable when you have the list of coupon codes that you want to assign out, but the set of contacts to whom you want to distribute them is defined by logic in Simon, making it impossible to allocate them via SQL in our Datasets product.
Getting Started
(1. contact your account team to get journey node unique id enabled, 2. create a lookup table that corresponds with the campaign in which you'd like to assign coupons, 3. build a journey that includes a step where this campaign exists, 4. as part of the Simon Template or custom context sent to another channel, include the snippet prescribed...)
Step 1
Contact your account team to get Journeys Node Unique ID turned on in your account, as we leverage that functionality to allocate coupon codes 1:1.
Step 2
Create a Lookup table that contains your coupon codes. The key into that lookup table should be an integer from 1...N (with N being the total number of coupon codes you have). Here's an example:
If we have our coupon codes in a table called coupons
that has the coupon code itself plus the date it was added , we can build a Lookup table named coupon_codes
like so:
select
coupon_code,
row_number() over (order by date_added) as lookup_key
from coupons
Sorting by date_added
isn't strictly necessary for one time blasts, but can help if you're adding more coupon codes to the same lookup table over time.
This will create a table with 1...N as the values for lookup_key
(with N
being the number of coupons available).
This solution does NOT cover coupon generation. It requires a table of pre-populated coupon codes available in your CDW.
Take a peek at the Advanced section below for leveraging more complex lookup keys (say, if you want to use the same Lookup table for coupon codes for different variants).
Step 3
Build your Journey. You can build a journey with as much internal complexity as you'd like, including leveraging branching and experimentation.
This feature is only available for Journeys
Step 4
Leverage the Journeys Node Unique ID in your content. We allocate coupon codes 1:1 by leveraging our Journeys Node Unique ID feature with Lookup tables. The Journeys Node Unique ID functionality ensures that each contact at each node in a Journey is assigned a unique number, starting at 1 for the first contact entering that node and increasing by 1 for each subsequent contact. By matching those numbers up with the numbers in the Lookup table (i.e. by using the Journey Node Unique ID as the key for your Lookup) you can achieve 1:1 coupon code assignment. Here's an example:
In order to use the couponing at any node in your Journey, you can leverage the Lookup you built in custom context by indexing into it using the contact_journey_node_unique_id
:
{{ lookups.coupon_codes[simon.contact_journey_node_unique_id] }}
This will assign a unique row from the coupons
table to each contact.
If you want multiple nodes of your Journey to leverage couponing (say, if you want to experiment with different discount amounts) you can do that -- each node's contact_journey_node_unique_id
is independent from one another. You'll also need to build a different Lookup like the above for each node leveraging coupons that contain the coupon codes you want to assign to the contacts at that node.
Using the same Lookup table in multiple Journey nodes will result in duplicate codes being sent, unless you're following the Advanced instructions for more complex Lookup keys
Step 5
Launch your Journey.
Step 6
Stop and drain your Journey.
1:1 coupon assignment only works out of the box for "one time Journeys." To set up a continuously syncing Journey with 1:1 coupon assignment, read the Advanced section below and speak to your account team.
A better workflow for one time Journeys is on our roadmap!
Edge Cases
Too many coupon codes
In the case where you have more coupon codes than contacts, you don't need to do anything. The extra coupon codes will simply go unused!
Too many contacts
In the case where you have more contacts than coupon codes, you'll need to catch Lookup errors. This case will manifest itself by a contact's simon.contact_journey_node_unique_id
value being higher than any of the keys in your coupon lookup table. You can catch this error in Jinja and handle it however you'd like -- perhaps with skip_action()
to prevent the send from occurring.
Skip Actions
Note that the contact_journey_node_unique_id
increments from one contact to the next regardless of whether or not a message was sent. That means that if the content you're sending contains skip_action()
statements that are frequently called, you will end up skipping coupon codes in your corresponding lookup table.
Advanced
Using more complex Lookup keys
The key to your Lookup table doesn't only need to be a single integer. You can concatenate that single integer with whatever other information you want to form more complex Lookup keys. Say, for example, that you have multiple variants of a couponing campaign you want to launch, one for 10% off and one for 20% off. You could put all the coupon codes in the same Lookup by using this SQL instead of the SQL above (assuming a field called pct_off
in your coupons
table):
select
coupon_code,
'10off_' || to_varchar(row_number() over (order by date_added)) as lookup_key
from coupons
where pct_off = 10
union
select
coupon_code,
'20off_' || to_varchar(row_number() over (order by date_added)) as lookup_key
from coupons
where pct_off = 20
You can then leverage this single Lookup in content with this bit of Jinja:
{{ lookups.coupon_codes['10off_' ~ simon.contact_journey_node_unique_id] }}
{{ lookups.coupon_codes['20off_' ~ simon.contact_journey_node_unique_id] }}
Each set of coupon codes will be numbered from 1...N based on the number of coupon codes of that type, and provided you use each bit of Jinja in a different Journey node, the Journey counters will be independent as well.
Continuously syncing Journeys
The big challenge with this workflow for continuously syncing Journeys is updating the Lookup table of coupon codes a) as coupon codes are assigned to contacts and b) as new, fresh coupon codes are added. It is possible to do this, but it requires some configuration across your Simon instance.
You can update your Lookup table to remove coupon codes that are assigned to contacts by leveraging Contact Journey History. Add a field to Contact Journey History Metadata that records the coupon code you're sending to the customer (the Jinja is the same as above -- {{ simon.contact_journey_node_unique_id }}
will render the same across both content and Contact Journey History, as it's the same across the entire node). Then, remove those codes from your Lookup table.
That on its own isn't enough, though. You also need to increment the lookup_key
by the number of coupon codes that have already been used. The contact_journey_node_unique_id
value doesn't reset to 0, so we need to update the baseline of the lookup_key
value to keep pace.
The SQL might look something like this:
with sent_coupon_codes as (
select
distinct metadata:coupon_code as coupon_code
from contact_journey_history
where journey_id = <your Journey ID>
and step_id = <the step of the Journey where coupons are sent>
),
used_coupon_count as (
select count(1) as used_coupon_count
from sent_coupon_codes
)
select
coupon_code,
used_coupon_count.used_coupon_count + row_number() over (order by date_added) as lookup_key
from coupons
join used_coupon_count
on 1 = 1
left join sent_coupon_codes
on sent_coupon_codes = coupons.coupon_code
where sent_coupon_codes.coupon_code is null
This will exclude coupon codes from your Lookup table that have already been used. In order to make sure this works effectively, though, you must ensure your Journey only syncs daily -- i.e. less frequently than your Lookup tables update.
This will ensure three key attributes about your Lookup table:
- The Lookup will only contain coupon codes that haven't been used yet (assuming the timing of your Journey and Lookup table update is correct)
- The Lookup's
lookup_key
will start at the next value defined by thecontact_journey_node_unique_id
- The Lookup will safely add new coupon codes that were added to the underlying
coupons
table
Updated 2 months ago