A HubDB Rules Table and Four Suppression Windows for Lead Source
More than thirty hand-written Marketo rules matched a contact's UTM and referrer values to a lead source. None ported literally, because a suppression layer on top of them stopped a later touch from overwriting an earlier source. We rebuilt the rules as one HubDB table and split the four suppression windows across native workflows and a custom-coded timestamp check.
Executive Summary
Context
A global enterprise SaaS company consolidating its demand generation stack onto HubSpot depended on accurate lead-source attribution to route and score inbound contacts reaching its Salesforce CRM. More than thirty Marketo rules, plus a suppression layer preventing a later touch from overwriting an earlier source, had to move onto HubSpot unchanged.
What We Built
We built a HubDB table keyed on lead_source, lead_source_channel and lead_source_offer that replaces the legacy rules with one governed lookup, plus a four-window suppression layer. Two windows run on native boolean-flag-and-delay workflows; the two one-hour windows run on a stored timestamp checked by custom code instead.
Tech Stack
- HubSpot
- HubDB
- Marketo
- Google Tag Manager
Not a fit if all of your suppression windows can run on native workflows alone, since the hybrid design adds custom code for two of them. It also assumes UTM data reliable enough for one rules table, and a willingness to run the new layer alongside the legacy one.
The Challenge
Lead-source matching in Marketo ran on more than thirty separate UTM and referrer rules. Each was a potential source of drift if a campaign's tagging changed and nobody updated the rule that read it.
A four-window suppression design sat on top: twelve hours against a paid touch, one hour each on a form submission and a qualified event, and twenty-four hours on record creation. The two one-hour windows needed a way to ask whether a field changed within the last hour.
Our Approach
We turned the Marketo rules into one HubDB table keyed on lead_source, lead_source_channel and lead_source_offer, so reclassifying a channel is an edit to one row instead of a search through dozens of rules. Google Tag Manager still delivers the values the table matches against.
We split the four windows between native workflows and custom code. The twelve-hour and twenty-four-hour windows run as boolean-flag-and-delay workflows: a property flips true, a timed delay holds it for the window, and the flag blocks or clears the write when the delay ends.
The two one-hour windows write a timestamp on the triggering event and check elapsed time with custom code. The client runs this layer alongside legacy Marketo logic.
Impact
One governed table replaces thirty scattered rules
Every rule that used to live as its own Marketo rule now reads from a single HubDB table. Reclassifying a channel is a row edit, not a search through more than thirty separate rules for the one that needs to change.
The two one-hour windows run on a stored timestamp
The one-hour form-submission and qualified-event guards run against a stored timestamp checked by custom code. A later touch inside that hour is suppressed instead of overwriting the source HubSpot already recorded.
The twelve-hour and twenty-four-hour windows stay on native workflows
The twelve-hour and twenty-four-hour windows stayed on native boolean-flag-and-delay workflows, with no custom code. Custom code was reserved for the two one-hour windows.
Go-live carries no unproven dependency on the new suppression layer
The new layer runs alongside legacy Marketo logic instead of replacing it outright at cutover. If the rebuild behaves unexpectedly against live traffic, the existing system is still the one setting lead source.
More than thirty Marketo UTM and referrer rules were consolidated into one HubDB table keyed on lead_source, lead_source_channel and lead_source_offer. Google Tag Manager still delivers the values the table matches against; one governed lookup now does the matching.
Four windows guard against a later touch overwriting an earlier lead-source value: twelve hours after a paid touch, one hour each after a form submission and a qualified event, and twenty-four hours after record creation. Each triggers on its own event.
The twelve-hour and twenty-four-hour windows run as native boolean-flag-and-delay workflows. The two one-hour windows write a timestamp on their triggering event and check elapsed time with custom code instead.
Rather than gate go-live on the new suppression layer, the client kept legacy Marketo lead-source logic running in parallel, proving the rebuild against live traffic with the existing system still in force.
The two one-hour suppression windows, on form submissions and qualified events, needed a check against a rolling hour. We split the four windows between native workflows and custom code. The twelve-hour and twenty-four-hour windows run as boolean-flag-and-delay workflows, and the two one-hour windows store a timestamp and check elapsed time with custom code. A later touch inside that hour is now suppressed instead of overwriting the lead source already recorded.
FAQ
The two one-hour windows write a timestamp on the triggering event and check elapsed time with custom code. The twelve-hour and twenty-four-hour windows stay on native workflows.
The rebuilt layer hadn't yet run against a full cycle of live traffic, so the client runs it alongside legacy Marketo logic rather than cutting over in one step, keeping a working fallback while the new table and its windows prove themselves.