hubspot-workflows-design
HubSpot Workflows Design
HubSpot workflows automate repeatable actions: setting properties, sending emails, creating tasks, routing leads, updating lifecycle stages, and triggering notifications. Every B2B SaaS HubSpot instance needs 10-20 core workflows. Beyond 50 active workflows, maintenance becomes a full-time job. Design for clarity and restraint.
The principle: a workflow should do one job. If the workflow description takes more than one sentence, it's probably doing too much. Split it.
Workflow Types
HubSpot supports 5 workflow types based on the object that triggers them.
| Type | Triggers on | Use for | Available on |
|---|---|---|---|
| Contact-based | Contact property change, form submission, list membership, page view | Lifecycle transitions, lead routing, nurture, scoring actions | All paid tiers |
| Company-based | Company property change, associated contact actions | ABM workflows, account-level automation | Professional+ |
| Deal-based | Deal property change, deal stage change | Sales process automation, notifications, task creation | Professional+ |
| Ticket-based | Ticket property change, ticket creation | Support automation, SLA alerts | Service Hub Professional+ |
| Custom object-based | Custom object property change | Enterprise-specific automation | Enterprise only |
Type selection rules:
- Use contact-based workflows for anything about a person: lifecycle stage, lead routing, nurture enrollment, scoring
- Use deal-based workflows for anything about a deal: stage-change notifications, task creation per stage, stale-deal alerts
- Use company-based workflows for account-level actions: ABM tier assignment, target account flagging, company lifecycle updates
- Don't use contact-based workflows to track deal-level processes. "Contact filled out a form" is contact-based. "Deal moved to proposal stage" is deal-based. Mixing them creates maintenance nightmares
The 15 Essential Workflows for B2B SaaS
Category 1: Lifecycle management (4 workflows)
1. Lead → MQL promotion
Trigger: Contact-based
Enrollment: Lifecycle stage IS Lead
AND (HubSpot Score >= 50
OR Contact submitted form "Demo Request"
OR Page views contain "/pricing" >= 2 times)
Actions:
1. Set Lifecycle Stage → MQL
2. Set Lead Status → New
3. Set property "MQL Date" → today
4. Send internal notification to contact owner
Re-enrollment: OFF (once per contact)
2. MQL → SQL promotion
Trigger: Contact-based
Enrollment: Lifecycle stage IS MQL
AND Lead Status IS "Qualified"
Actions:
1. Set Lifecycle Stage → SQL
2. Set Lead Status → New (reset for AE)
3. Set property "SQL Date" → today
4. Send internal notification to assigned AE
Re-enrollment: OFF
3. Contact recycling
Trigger: Contact-based
Enrollment: Lead Status IS "Unqualified" OR "Bad Timing"
AND Lifecycle stage IS MQL OR SQL
Actions:
1. Set Lifecycle Stage → Lead
2. Set Lead Status → Recycled
3. Set property "Recycle Date" → today
4. Enroll in re-nurture email sequence (delay 30 days)
5. Clear contact owner (returns to pool)
Re-enrollment: ON (contact can be recycled multiple times)
4. Deal-based lifecycle sync
Trigger: Deal-based
Enrollment: Deal stage changed
Actions:
If deal stage = Closed Won:
1. Set associated contacts' lifecycle stage → Customer
2. Set associated company lifecycle → Customer
3. Send internal notification to CS team
If deal stage = Closed Lost:
1. Set property "Loss Reason" (require on stage change)
2. Set property "Lost Date" → today
3. If loss reason IS "Bad Timing": enroll contacts in recycle flow
Re-enrollment: ON (multiple deals per contact possible)
Category 2: Lead routing (3 workflows)
5. New MQL routing (round-robin)
Trigger: Contact-based
Enrollment: Lifecycle stage IS MQL
AND Lead Status IS "New"
AND Contact Owner IS empty OR IS "Unassigned"
Actions:
1. Rotate among SDR team:
- SDR A (33%)
- SDR B (33%)
- SDR C (34%)
2. Set Contact Owner → assigned SDR
3. Create task for assigned SDR: "Follow up with new MQL"
Due: 1 hour from now
4. Send Slack notification to assigned SDR
Re-enrollment: OFF
6. Territory-based routing
Trigger: Contact-based
Enrollment: Lifecycle stage IS MQL
AND Lead Status IS "New"
AND Country IS known
Actions:
If/then branch:
If Country IS US AND State IS CA, WA, OR:
Set owner → AE West
If Country IS US AND State IS NY, MA, CT:
Set owner → AE East
If Country IS GB, DE, FR:
Set owner → AE EMEA
Default:
Set owner → SDR Pool (round-robin)
Re-enrollment: OFF
7. Account-based routing (existing account)
Trigger: Contact-based
Enrollment: Lifecycle stage changes to MQL
AND Associated company has property "Company Owner" IS known
Actions:
1. Copy "Company Owner" to "Contact Owner"
(routes to existing account owner instead of round-robin)
2. Create task for Company Owner: "New contact at owned account"
3. Send Slack notification
Re-enrollment: OFF
Category 3: Speed-to-lead (2 workflows)
8. High-intent instant alert
Trigger: Contact-based
Enrollment: Contact submitted form "Demo Request"
OR Contact submitted form "Contact Sales"
OR Contact viewed page "/pricing" >= 3 times in 1 session
Actions:
1. Set Lifecycle Stage → MQL (if currently Lead)
2. Route per routing workflow (#5, #6, or #7)
3. Send Slack alert to #sales-alerts:
"[Name] from [Company] just [action]. Follow up NOW."
4. Create task: "Call [Name] - high intent"
Due: 15 minutes from now
5. Send auto-reply email: "Thanks for reaching out"
Re-enrollment: OFF
9. MQL follow-up SLA escalation
Trigger: Contact-based
Enrollment: Lifecycle stage IS MQL
AND Lead Status IS "New"
AND "MQL Date" IS more than 4 hours ago
Actions:
1. Send notification to SDR manager:
"MQL [Name] has been unworked for 4+ hours"
2. If still "New" after 24 hours:
Send escalation to VP Sales
3. If still "New" after 48 hours:
Reassign to backup SDR
Re-enrollment: OFF
Category 4: Nurture (3 workflows)
10. New lead nurture
Trigger: Contact-based
Enrollment: Lifecycle stage IS Lead
AND Lead Source IS NOT "Outbound" (don't nurture outbound prospects)
Actions:
Email 1 (immediately): Welcome + top resource
Delay 3 days
Email 2: Case study relevant to their industry (use smart content)
Delay 5 days
Email 3: Webinar invite or product overview
Delay 7 days
Email 4: Soft CTA - "worth a quick look?"
Goal: Lifecycle stage changes to MQL (exit workflow on conversion)
Re-enrollment: OFF
11. Recycled lead re-nurture
Trigger: Contact-based
Enrollment: Lead Status IS "Recycled"
AND "Recycle Date" IS more than 30 days ago
Actions:
Delay 30 days (cooling-off period)
Email 1: New content or product update (not the same content they saw before)
Delay 14 days
Email 2: Industry benchmark or peer story
Delay 14 days
Email 3: Soft re-engagement: "Anything changed on [original pain point]?"
Goal: Contact reaches MQL score threshold (exit and re-promote)
Re-enrollment: ON (can re-enter if recycled again)
12. Post-demo no-show recovery
Trigger: Contact-based
Enrollment: Meeting outcome IS "No Show"
AND Lifecycle stage IS SQL OR Opportunity
Actions:
Email 1 (immediately): "Missed you today - here's the recording/recap"
Delay 2 days
Email 2: "Still interested? Here are 3 times that work"
Delay 5 days
Email 3: "Should I close the loop?"
If no response after all 3: Set Lead Status → "Bad Timing"
Re-enrollment: ON (per meeting, not per contact)
Category 5: Deal management (2 workflows)
13. Stale deal alert
Trigger: Deal-based
Enrollment: Deal stage IS NOT Closed Won or Closed Lost
AND Last activity date IS more than 14 days ago
Actions:
1. Send notification to deal owner:
"Deal [Name] has had no activity in 14 days. Update or close."
2. Create task: "Update stale deal [Name]"
Due: 2 days from now
3. If no activity after 7 more days (21 total):
Send escalation to sales manager
Re-enrollment: ON (re-triggers if deal goes stale again)
14. Deal stage task creation
Trigger: Deal-based
Enrollment: Deal stage changed
Actions:
If/then by stage:
Discovery → Create task: "Complete discovery call prep"
Demo → Create task: "Send demo follow-up within 24 hours"
Proposal → Create task: "Send proposal within 48 hours"
Negotiation → Create task: "Review contract terms with legal"
Verbal Commit → Create task: "Send contract for signature"
Re-enrollment: ON (triggers each time stage changes)
Category 6: Data hygiene (1 workflow)
15. Zombie deal cleanup
Trigger: Deal-based
Enrollment: Close date IS in the past
AND Deal stage IS NOT Closed Won or Closed Lost
Actions:
1. Send notification to deal owner:
"Deal [Name] has a close date in the past. Update or close."
2. Delay 7 days
3. If still open with past close date:
Set deal stage → Closed Lost
Set loss reason → "Stale - Auto-closed"
Send notification: "Deal [Name] auto-closed as stale"
Re-enrollment: OFF
Workflow Design Rules
Naming convention
Use a consistent format: [Category] - [Object] - [Action]
Examples:
Lifecycle - Contact - Lead to MQL PromotionRouting - Contact - New MQL Round RobinNurture - Contact - New Lead DripDeal Mgmt - Deal - Stale Deal AlertHygiene - Deal - Zombie Cleanup
Naming rules:
- Category prefix makes workflows sortable and scannable in the workflow list
- Include the object type. "Lead to MQL" is ambiguous. "Contact - Lead to MQL" is clear
- Never name a workflow "Test" or "Copy of [other workflow]." Rename before activating
Enrollment and re-enrollment
| Scenario | Re-enrollment setting | Why |
|---|---|---|
| Lifecycle promotion (Lead → MQL) | OFF | A contact should only be promoted once per journey |
| Lead routing | OFF | Route once. Don't re-route on every property update |
| Nurture sequence | OFF (typically) | Don't re-enroll someone in the same drip they already completed |
| Recycling | ON | A contact can be recycled multiple times from different stages |
| Stale deal alert | ON | A deal can go stale, get updated, and go stale again |
| Deal stage tasks | ON | Tasks should fire each time the stage changes |
If/then branches
- Keep branches to 3-5 max per workflow. Beyond 5, create separate workflows
- Always include a default/else branch. Contacts that don't match any branch should still get handled (even if the action is "do nothing and log it")
- Name each branch. "Branch 1" is unreadable. "US East Territory" is clear
Delays
- Use "Business days" for sales-related delays. A 3-day delay on Friday fires Monday, not Sunday
- Don't chain more than 5 delays in one workflow. Long sequences with many delays are hard to debug and maintain. Use HubSpot sequences (separate feature) for multi-step email cadences
- Test delay timing by enrolling a test contact and watching the timeline
Workflow Maintenance
Monthly review checklist
- [ ] Count active workflows. If > 50, audit for duplicates and consolidation
- [ ] Check workflow errors (Settings → Workflows → Errors). Fix any enrollment or action failures
- [ ] Review enrollment counts. Workflows with 0 enrollments in 30 days may be obsolete
- [ ] Check for conflicting workflows (two workflows setting the same property to different values)
- [ ] Verify notification recipients are still on the team (people leave, workflows keep notifying)
- [ ] Review suppression lists. Contacts in suppression should be there intentionally
Workflow documentation
For every active workflow, maintain a one-line entry in a shared doc:
| Workflow name | Object | Purpose | Owner | Last reviewed |
|--------------|--------|---------|-------|---------------|
| Lifecycle - Contact - Lead to MQL | Contact | Promotes leads to MQL on score threshold | RevOps | 2026-04-01 |
Workflow Limits and Gotchas
| Gotcha | What happens | How to avoid |
|---|---|---|
| Workflow loops | Workflow A sets a property that triggers Workflow B, which sets a property that triggers A again | Add enrollment conditions that prevent re-triggering. Check for circular dependencies before activating |
| Rate limits | HubSpot processes ~100 workflow actions per 10 seconds per portal. High-volume workflows queue | Don't enroll large lists (10K+) in instant workflows. Use scheduled drip enrollment |
| Suppression exhaustion | A contact hits the suppression list and blocks all future enrollment in that workflow | Use separate workflows per suppression need instead of one giant workflow with suppression |
| Association quirks | Deal-based workflows can't directly update contact properties without a "copy property" action or custom code | Use "Copy to associated contact" actions or Operations Hub custom code |
| Workflow goal vs unenrollment | Goal = exit condition (contact converted). Unenrollment = removed from workflow for a different reason | Use goals for positive outcomes (became MQL). Use unenrollment for negative outcomes (opted out) |
| Backdated data | Changing enrollment criteria on an existing workflow doesn't retroactively enroll contacts that already matched | To catch historical contacts, use "Enroll existing contacts" option when editing enrollment |
Anti-Pattern Check
- 80+ active workflows with no naming convention. Unmanageable. Consolidate to 15-25 core workflows. Name with category prefix
- One workflow doing 6 things. A workflow that routes leads, sets lifecycle stage, sends a nurture email, creates a task, and updates a custom property is unmaintainable. Split into separate single-purpose workflows
- Re-enrollment ON for lifecycle promotions. A contact should not be promoted from Lead to MQL every time their score changes. Once promoted, the workflow should not re-fire. Set re-enrollment OFF
- No default branch in if/then logic. Contacts that don't match any condition silently fall out of the workflow. Always handle the "else" case, even if the action is just "set a flag for review"
- Notification workflows with no SLA. "Notify SDR about new MQL" without a follow-up escalation is a notification that gets ignored. Add an escalation step if no action is taken within the SLA
- Test contacts left in production workflows. Test contacts trigger real notifications, real tasks, and real email sends. Remove test contacts from workflows before going live. Use a test portal when possible
- Workflows modifying the same property from different triggers. Workflow A sets lead status to "New" and Workflow B sets it to "In Progress" based on different triggers. When both fire on the same contact, the result is unpredictable. Consolidate property updates into one workflow with branching
- No workflow documentation. When the person who built the workflows leaves, nobody knows what they do. Document every active workflow in a shared sheet with purpose, owner, and last review date