A global trigger campaign to alert on interesting Program Status changes, with the new Status included in the alert

Marketo recently added a bunch of new {{trigger.tokens}} that can help reduce the number of nearly-redundant trigger campaigns, always a good thing.

Some of the tokens are relatively “raw,” though: they output unique IDs, not friendly names. Massaging those into values suitable for people like sales owners takes a little work.

Going global to avoid local bloat

We had a request the other day for alerts on Program Status is Changed, where the New Status is in a list of interesting statuses and the program is any program in the instance. The alert email also has to include the new status.

Such a campaign can of course be added at the program level:

  • trigger on Program Status is Changed
  • constrain on Program [is] <this program>, New Status [is] <list>
  • include {{member.status}} in the email

The problem is you’d have to do this for every program, forever. Nobody wants that.

Instead, we wanted a single global campaign triggering on Program Status is Changed, Program [is any]:

The goal, then, is to figure out how to get the equivalent of {{member.status}}, since our campaign lives outside the triggering program(s).

Aha! One new token is {{trigger.New Status ID}}, a token that’s available in a Program Status is Changed flow. Sounds perfect, right? But the catch is {{trigger.New Status ID}} returns the unique numeric identifier of the status, not its name. And sales doesn’t understand Program Status 113 or Program Status 221!

Start thinking about Velocity maps

One ultra-powerful use for Velocity is to map unfriendly names/IDs to friendly names, like so:

#set( $statusNameMap = {
  10  : "Advertising > Filled Form",
  56  : "Advertising > Downloaded",
  224 : "Advertising > Downloaded High-Value Asset",
  168 : "Content > Downloaded",
  221 : "Content > Downloaded High-Value Asset",
  90  : "Content Syndication (PPL) > Downloaded",
  225 : "Content Syndication (PPL) > Downloaded High-Value Asset",
  226 : "Content Syndication (PPL) > Filled Form",
  4   : "Webinar > Registered",
  6   : "Webinar > Attended",
  7   : "Webinar > On-Demand",
  210 : "Website > Filled Form",
  211 : "Website > Downloaded", 
  231 : "Website > Downloaded High-Value Content"
} )

(Some call these as “lookup tables” but I prefer to call them “maps,” since they’re literally Java LinkedHashMaps.)

If we had such a map, we could grab the friendly name with $statusNameMap.get($programStatusId).

There’s no official way to get the Program Status ID that currently[1] corresponds to a given Program Status Name. You can’t get it via the REST API and there isn’t a formal export feature. But you can use Dev Tools to get it from the Marketo UI.[2] Right-click and inspect the <input> for the Program Status Name:

Then look in the HTML for the adjacent <input type="hidden">. Its value attribute is the Program Status ID.

OK, great! Now we can build that IDName map in a Velocity token {{my.Program Status Name Map}}. But there’s one more hurdle.

{{trigger.tokens}} aren’t automatically accessible in Velocity

So we have $statusNameMap ready to go. But how do we look up our {{trigger.New Status ID}} in the map, since {{trigger.tokens}} aren’t present in the Velocity context?

Well, they’re actually present, but they’re tricky to get to. Each one is a property of the Velocity $trigger object, but:

  • it’s converted to upper case
  • spaces are converted to underscores
  • it’s prefixed with v
  • it’s suffixed with _ and an unpredictable number

For example, {{trigger.New Status ID}} can be $trigger.vNEW_STATUS_ID_99 in Velocity. But the 99 can’t be known in advance.

Not to worry. We can’t directly get("vNEW_STATUS_ID_<nnnn>") because we don’t know the suffix, but we can loop over $trigger until a key matches our prefix.

That happens in the Velocity token {{my.Get Program Status Name}}:

#foreach( $entry in $trigger.entrySet() )
#set( $key = $entry.getKey() )
#set( $value = $entry.getValue() )
#if( $key.startsWith("vNEW_STATUS_ID_") && !$display.alt($value,"").isEmpty() )
#set( $newProgramStatusId = $convert.toInteger($value) )
#break
#end
#end
#set( $newProgramStatusName = $statusNameMap.get($newProgramStatusId) )

The final piece is the one-line Velocity token {{my.New Program Status Name}} to output the value (remember, best practice is separate “calculating” and “output” tokens):

${newProgramStatusName}

Wait, one more thing

The {{trigger.token}} must be used in the email to be accessible in Velocity. Sounds like a Catch-22, but you can drop it inside an HTML comment![3]

Sample alert email

Subject: One of your contacts just had an interesting Program Status Change!
{{my.Program Status Name Map}}{{my.Get Program Status Name}}
Name: {{lead.Full Name}}
Email: {{lead.Email Address}}
Company: {{company.Company Name}}
Title: {{lead.Job Title}}
Phone: {{lead.Phone Number}}
Program Name: {{trigger.name}}
New Program Status: {{my.New Program Status Name}} ({{trigger.New Status ID}})
Salesforce Record Link: https://exampleco.my.salesforce.com/{{lead.SFDC Id}}

The “map” token {{my.Program Status Name Map}} and “calculate” token {{my.Get Program Status Name}} must be included somewhere before the “output” token {{my.New Program Status Name}}.

Notes

[1] Yes, it’s technically possible for the name to change (that’s why the Program Status ID exists). And of course new statuses may be added. You’ll have to modify the $statusNameMap if there’s a relevant change to Admin » Tags » Channels.

[2] Alternately, look at the POST response for https://app-<nnnn>.marketo.com/marketingEvent/getDescriptorValue. This is left as an exercise for the reader.

[3] Or between two partial Velocity tokens (#* and *#) to wrap it in a block comment. This has the advantage of removing it completely from the rendered email, both HTML and Text versions.