Quick Takeaways
- Marketo click tracking scans email HTML for protocols before inserting tokens.
- The fix: output the full anchor tag from a Velocity script.
- A lead field token placed in an href records zero clicks.
- Marketo builds emails in two passes: scan first, then tokens.
- Hardcode the protocol outside the Velocity variable, every time.
- Each tracked link needs its own separately named Velocity variable.
Table of Contents

You launch a webinar invitation, the links preview perfectly, and recipients click straight through to the right registration page. Then the Email Performance Report shows zero clicks. If you have ever chased that discrepancy, you have hit one of the least intuitive limits in Marketo click tracking.
On a healthy build, this tracking is invisible and automatic. Marketo wraps every destination URL with a redirect through its tracking server, logs the click on the lead record, and forwards the recipient onward. That reliability is exactly why the failure is so disorienting: the moment a link comes from a lead field instead of static HTML, tracking can vanish without a single warning.
This case study walks through a real webinar-invite scenario for a wealth-management client: four reasonable approaches that silently failed, the architectural reason why, and the one Velocity-based fix that restores tracking on per-recipient links.
The Requirement: A Dynamic Per-Recipient Link From a Lead Field
Picture a common setup on your own instance. A marketing operations team needs to send webinar invitations where every recipient receives a unique registration link. Each link lives in a lead field called Webinar Url (webinarUrl), populated upstream by an integrated platform such as Bizzabo. The field arrives as a complete URL, protocol included:
https://events.example.com/register?id=abc123
The goal sounds trivial: render that lead field token as a tracked, clickable link for each recipient. Instead, a standard lead field token Marketo setup quietly falls apart, and every obvious path leads to the same dead end.
Where It Breaks: Four Approaches That Silently Fail
Each attempt below works for the recipient and fails for the marketer. The link renders, the destination is correct, and Marketo click tracking records nothing.
Approach 0: Native hyperlink via the Design Editor
The most natural attempt needs no code. In the email Design Editor, a marketer selects text, opens the hyperlink dialog, and pastes the token {{lead.webinarUrl}} into the URL field. The email renders correctly and recipients reach the right page, yet no click activity is logged. Behind the scenes the editor writes:
<a href="{{lead.webinarUrl}}">Link Text</a>
No warning, no error, no hint that the link will not be tracked, and the gap only surfaces after send.
Approach 1: Lead token directly in the href (HTML editor)
Dropping the token straight into the markup produces the identical silent failure:
<a href="{{lead.webinarUrl}}">Register Now</a>
Same render, same correct destination, same zero clicks. This is a different failure mode from a related Marketo tracking failure where shared lead fields overwrite each other across programs. That problem is about field governance; this one is about the sequence in which Marketo assembles the email.
Approach 2: Protocol hardcoded outside a My Token
Marketo documents a workaround for My Tokens: store only the path and put the protocol in static HTML.
<a href="https://{{my.webinarUrl}}">Register Now</a>
It genuinely works for My Tokens, because the protocol sits in the static HTML where tracking can see it. It is not viable here: the value is a Marketo dynamic URL sourced from a lead field, and the upstream integration writes the full protocol into that field, so you cannot store a bare path.
Approach 3: Velocity script stripping the protocol into a My Token
The next attempt uses a Velocity script token to strip the protocol and output only the bare path, then hardcodes the protocol outside the token:
#set( $rawUrl = $lead.webinarUrl )
#set( $urlNoProto = $rawUrl.replaceAll("(?i)^https?://", "") )
${urlNoProto}
<a href="https://{{my.customlink}}">Register Now</a>
Still not tracked. Velocity script token output is inserted during the same pass as every other token, which runs after tracking has already been applied. Splitting the protocol outside the token only helps when that protocol sits in static HTML.
Root Cause: Marketo’s Two-Pass Email Build
Every failure above traces to one architectural fact: the Marketo two-pass email build. Marketo assembles each email in two sequential passes, and their order is the whole story.
Marketo’s tracking pass scans the email HTML for http:// or https:// before token values are inserted, so any URL that arrives via a token is never wrapped for tracking.
In plain sequence:
| Pass | What happens |
|---|---|
| Pass 1 | Marketo scans the HTML for http:// or https:// and wraps matching URLs with tracking redirects. |
| Pass 2 | Token and Velocity script values are inserted into the HTML. |
Any URL that does not exist as a literal, protocol-bearing string during Pass 1 will never be tracked, whether it arrives via a lead token, a My Token, or Velocity output. Adobe corroborates that Marketo operates on the link as it appears in the static HTML in its guidance on how tracked links behave when tracking is enabled. If the protocol is not visible at scan time, there is nothing to wrap.
The Fix: Output the Full Anchor Tag From Velocity
The only reliable Marketo Velocity script tracking solution is to output the entire anchor tag from within a Velocity script, with the protocol hardcoded as a literal outside the variable and the full path held in a Velocity variable. Structured this way, Marketo’s tracking engine can introspect the output and apply tracking.
#set( $rawUrl = $lead.webinarUrl )
#set( $urlNoProto = $rawUrl.replaceAll("(?i)^https?://", "") )
#if( $rawUrl.matches("(?i)^http://.*") )
<a href="http://${urlNoProto}">Link Text</a>
#elseif( $rawUrl && $rawUrl != "" )
<a href="https://${urlNoProto}">Link Text</a>
#end
Why this works
- Protocol is literal: the http:// or https:// is a hardcoded string in the href, so the tracking pass sees a real URL.
- Variable carries the path: only ${urlNoProto} is dynamic, and Marketo processes the full anchor tag from Velocity before applying tracking.
- Null check protects the send: the #if/#elseif guard prevents a broken anchor tag when the field is empty.
Adobe’s Marketo email scripting documentation backs this pattern: build the complete link inside the script rather than feeding a bare token into a static href.
Additional Constraints to Respect
Even with the correct structure, three constraints from Adobe documentation and the Marketo Nation community will quietly break Marketo click tracking if you ignore them.
- Output the full anchor tag, including the closing </a>, from Velocity. Partial output of the URL alone does not work.
- Do not generate tracked links inside a for or foreach loop.
- Give each tracked link its own separately named Velocity variable. Reusing one variable for multiple links breaks tracking.
Outcome: What This Means for Your Instance
Once the anchor tag is emitted from Velocity with the protocol hardcoded outside the variable, clicks register normally on every lead record and Marketo click tracking reflects real engagement again in the Email Performance Report. The tracked links Marketo produces this way behave exactly like tracking on a static template link, so your reporting, engagement scoring, and any downstream sync stay accurate.
This dynamic-URL problem is one specific case of a broader measurement discipline, which we cover in our companion guide on how to stop double-tracking your emails. The practical takeaway is to treat any URL sourced from a lead field, custom object, or external integration as a special case from the start. The same discipline that keeps click data clean here also underpins accurate campaign measurement elsewhere, such as when you set up Marketo PPC integration for accurate tracking.
Conclusion
Marketo click tracking was built for static HTML emails where every URL is known at template time, and dynamic links sourced from lead fields simply fall outside that assumption. That is why the obvious approaches fail silently and why the two-pass build, not a configuration toggle, is the real culprit. Output the complete anchor tag from Velocity with the protocol hardcoded outside the variable, respect the three constraints, and per-recipient links track like any other. If you would rather have this diagnosed and hardened in your own instance, the team at 4Thought Marketing can help you get dynamic links tracking reliably.
About 4Thought Marketing
We're a B2B marketing automation and AI consultancy with a thing for getting complex tech to actually work. Since 2008, we've helped hundreds of organizations across financial services, technology, manufacturing, and real estate get more from Eloqua, Marketo, and their CRM integrations. We serve our clients across marketing automation strategy, lead lifecycle, AI, compliance, preference management, and more. Explore our services or get in touch.
Frequently Asked Questions
Why is Marketo showing zero clicks on my links?
The most common cause is a link whose URL comes from a token instead of static HTML. This is the classic Marketo click tracking failure: the tracking pass scans for http:// or https:// before token values are inserted, so a token-based URL is never wrapped and no click is logged.
Can Marketo track a link stored in a lead field?
Yes, but not by placing the lead field token directly in the href. You must output the entire anchor tag from a Velocity script, with the protocol hardcoded as a literal outside the variable and the path in a Velocity variable. Marketo can then introspect the output and apply tracking.
How do I make a Velocity-script link trackable in Marketo?
Build the complete anchor tag inside the Velocity script, hardcode http:// or https:// outside the dynamic variable, and include a null check so an empty field does not emit a broken tag. Output the full tag including the closing tag, and give each link its own named variable.
What is the two-pass email build in Marketo?
Marketo assembles emails in two sequential passes. Pass 1 scans the HTML for protocol-bearing URLs and wraps them with tracking redirects. Pass 2 inserts token and Velocity values. Any URL not present as a literal protocol string during Pass 1 is never tracked.
Why does hardcoding the protocol work for My Tokens but not lead fields?
With a My Token you can store only the path and place the protocol in static HTML, so tracking sees a real URL at scan time. A lead field populated by an integration usually contains the full protocol already, so you cannot split it. Emit the whole anchor tag from Velocity instead.
Do I need to change the URLs in my integration to fix this?
No. The fix lives entirely in how the email renders the link, not in the upstream data. Keep the full URL in the lead field and let a Velocity script emit the trackable anchor tag around it.



