Engagement-Based Suppression Workflow
Build a two-tier sunset workflow that re-engages dormant contacts before suppressing them. Tier 1 triggers a re-engagement campaign after a configurable inactivity window. Tier 2 suppresses contacts that fail to re-engage within a configurable re-engagement window.
Run this skill in Claude Code:
/plugin marketplace add tomgranot/hubspot-admin-skills
/plugin install hubspot-admin@hubspot-admin-skills
/engagement-suppression-workflowEngagement-Based Suppression Workflow
Build a two-tier sunset system that protects email deliverability while giving disengaged contacts a fair chance to re-engage before suppression.
Why Two Tiers Matter
Suppressing contacts immediately after inactivity is aggressive and loses potential re-activations. A two-tier approach:
- Tier 1 (inactive for your sunset window β typically 120-270 days): Triggers a re-engagement campaign β a last chance to interact.
- Tier 2 (your re-engagement window after Tier 1 β typically 21-45 days β with still no engagement): Suppresses the contact from marketing emails.
This preserves deliverability scores while maximizing the recoverable audience.
Prerequisites
- HubSpot Marketing Professional or Enterprise plan
- A HubSpot private app access token (
HUBSPOT_ACCESS_TOKENin.env) with theautomationscope (for the API path) - Python 3.10+ with
uv - A re-engagement email campaign or sequence ready to send
- A custom dropdown property to track suppression status (default:
engagement_flagβ the execute script creates it if missing)
Scripts
| Stage | Script | Run with |
|---|---|---|
| Before | scripts/before.py | uv run skills/engagement-suppression-workflow/scripts/before.py |
| Execute | scripts/execute.py | uv run skills/engagement-suppression-workflow/scripts/execute.py |
| After | scripts/after.py | uv run skills/engagement-suppression-workflow/scripts/after.py |
before.py inventories existing workflows and checks for name collisions. execute.py creates the engagement_flag property (if missing) and both sunset workflows via POST /automation/v4/flows β always disabled, for review in the UI before enabling. after.py verifies they exist and reports enabled state.
Workflow Design
TRIGGER: Last engagement date > [sunset window] ago
AND email is known
AND not globally unsubscribed
AND [suppression status property] is unknown
β
βΌ
ββββββββββββββββββββββ
β Set [status prop] β
β = "re-engagement β
β sent" β
ββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββ
β Enroll in β
β re-engagement β
β campaign/sequence β
ββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββ
β Delay: [re-engage β
β window] days β
ββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββ
β IF/THEN BRANCH: β
β Any engagement in β
β re-engage window? β
ββββββββββββ¬ββββββββββ
β YES β NO
β β
βΌ βΌ
Clear Set [status prop]
status = "suppressed"
prop + set non-marketingBuilding the Workflow: Two Options
Option A: Create via the v4 Automation API (primary)
The stable v4 Automation API supports creating workflows programmatically. Instead of one workflow with an if/then branch, the script decomposes the design into two linear workflows β which the API expresses cleanly and which are easier to reason about:
- Tier 1 β Flag for Re-engagement: enrollment = last open older than the sunset window (or never) AND email known AND not globally unsubscribed AND flag unknown. Action: set
engagement_flag= βre-engagement sentβ. - Tier 2 β Suppress Non-responders: enrollment = flag = βre-engagement sentβ AND still no open/click after sunset + re-engagement windows. Actions: set flag = βsuppressedβ, set marketing contact status to non-marketing.
Run the scripts (configure SUNSET_DAYS, REENGAGE_DAYS, FLAG_PROPERTY in execute.py first):
uv run skills/engagement-suppression-workflow/scripts/before.py
uv run skills/engagement-suppression-workflow/scripts/execute.pySafety model: both workflows are created with isEnabled: false. Review them in Automation > Workflows, then:
- Add the βenroll in re-engagement sequence/emailβ action to Tier 1 (email/sequence IDs are portal-specific).
- If the script reported it could not include the set-marketing-status action, add βSet marketing contact statusβ to Tier 2 in the UI (one click).
- Turn both on, choosing whether to enroll existing contacts.
Payload notes: the scripts use documented v4 action types (0-5 set property; 0-31 set marketing contact status, with automatic fallback if your portal rejects its undocumented field shape). If a filter shape is rejected, HubSpotβs own recommendation applies: build the workflow once in the UI, GET /automation/v4/flows/{flowId}, and adapt the script to the exact shapes your portal returns.
Option B: Manual UI Build (single workflow with branch)
Follow the step-by-step instructions in Stage 3 below. This builds the original single-workflow design with the if/then re-engagement branch, and works on portals where the Automation API is unavailable.
Alternatives: HubSpot Breeze AI can scaffold a similar workflow from a prompt, but it creates event-based (OR) triggers instead of filter-based (AND) triggers and cannot configure re-enrollment or βis unknownβ conditions β every Breeze-built trigger needs manual verification. The Claude Chrome extension can drive the workflow builder UI directly. Both are secondary to Options A and B.
Step-by-Step Build Instructions
Stage 1: Plan
- Choose your sunset window (typically 120-270 days; default 180) and re-engagement window (21-45 days; default 30).
- Define βengagementβ for the Tier 2 check. Recommended: email open OR email click within the re-engagement window.
- Create or identify a re-engagement email/sequence. A simple 1-2 email series asking βStill interested?β with a clear CTA works well.
Stage 2: Before
Run uv run skills/engagement-suppression-workflow/scripts/before.py β it inventories all workflows, flags name collisions with the two planned SUNSET workflows, and writes the inventory CSV baseline.
If building manually, also create your suppression status property (dropdown: βre-engagement sentβ, βsuppressedβ) if it does not exist β the API path creates it automatically.
Stage 3: Execute
Option A: run uv run skills/engagement-suppression-workflow/scripts/execute.py, then complete the UI review steps listed above.
Option B β manual UI build:
Set enrollment trigger:
hs_email_last_open_dateis more than your sunset window (typically 120-270 days) ago OR is unknown- AND
emailis known - AND
hs_email_optoutis not true - AND your suppression status property is unknown
Action: Set contact property
- Your suppression status property = βre-engagement sentβ
Action: Enroll in re-engagement sequence (or send re-engagement email)
Delay: your re-engagement window (typically 21-45 days)
If/then branch:
- Condition:
hs_email_last_open_dateis less than [re-engagement window] days ago ORhs_email_last_click_dateis less than [re-engagement window] days ago - YES (re-engaged): Set your suppression status property to blank/unknown (clears flag, contact returns to normal)
- NO (still disengaged): Set your suppression status property = βsuppressedβ and set
hs_marketable_statusto non-marketing contact via the βSet marketing contact statusβ workflow action. (The property itself is read-only via direct API writes β a workflow action is the mechanism, whether the workflow is built in the UI or created via the v4 API.)
- Condition:
Settings:
- Re-enrollment: OFF
- Goal: Contact opens or clicks any email (optional β exits workflow early)
Turn on the workflow.
Stage 4: After
- Run
uv run skills/engagement-suppression-workflow/scripts/after.py(API path) to confirm both workflows exist and report their enabled state. - Spot-check 10-20 contacts that entered the workflow. Confirm:
- Re-engagement email was sent
- After the re-engagement window, disengaged contacts were suppressed
- Re-engaged contacts had their suppression status property cleared (Option B) or never entered Tier 2 (Option A)
- Monitor deliverability metrics weekly for the first month.
- Track how many contacts re-engage vs. get suppressed β adjust the sunset window if needed.
Rollback
- Turn off the workflow.
- To reverse suppressions: filter contacts where your suppression status property = βsuppressedβ and manually set them back to marketing contacts in the UI.
- Clear the suppression status property values in bulk if needed.
Tuning
- Shorten the sunset window (e.g., 90-120 days) for aggressive deliverability improvement.
- Lengthen the re-engagement window (e.g., 45-60 days) if your email cadence is low.
- Exclude recent customers β add a filter to skip contacts with lifecycle stage = Customer or with a closed-won deal in the last 12 months.
Problems this skill solves
Related skills in Automation Workflows
/bounce-monitoring-workflowBuild a workflow to protect sender reputation through automated bounce monitoring. Auto-suppresses contacts above a configurable bounce threshold, alerts on hard bounces, and flags high-bounce contacts for weekly manual review.
/lifecycle-progression-workflowBuild workflows to automate contact progression through the sales funnel: Lead to MQL to SQL to Opportunity to Customer. Each transition is triggered by a specific event (score threshold, meeting booked, deal created, deal won).
/new-contact-hygiene-workflowBuild a HubSpot workflow that auto-enriches and stages new contacts upon creation. Sets lifecycle stage via the v4 Automation API, copies company name and industry from the associated company (UI step), and branches on completeness.
/workflows-as-codeExport all HubSpot workflows to versioned JSON files via the v4 Automation API, diff exports over time, and restore or recreate workflows from JSON. Treats workflow definitions like code: backed up, reviewable, and recoverable.