Clay workflows / Route leads

Revenanas Clay workflow guide

How to route HubSpot leads to the right rep in Clay

Build a tested Clay workflow that enriches an unowned inbound lead, selects an eligible rep, and prepares the HubSpot owner update without overwriting an existing assignment.

CRM inputClay workflow

Assign a new, unowned HubSpot lead to an eligible sales rep using fit, territory, segment, availability, and weighted capacity.

Manual input -> Normalize lead -> Existing owner? -> Hold existing owner OR Select eligible rep -> Rep found? -> Prepare HubSpot writeback OR Hold for RevOps review

To route HubSpot leads in Clay without overwriting owners, first stop any record that already has an owner. For unowned leads, derive a territory and commercial segment, filter the rep roster to active and available matches, select the rep with the lowest capacity-adjusted load, and only then prepare the HubSpot owner update. If nobody qualifies, hold the lead for RevOps review instead of assigning it arbitrarily.

A manual test trigger feeds one normalization step and two explicit rule branches. The first branch protects existing owners. The second filters and scores eligible reps, then either emits a HubSpot-ready payload or holds the record for review.

Flow: Manual input -> Normalize lead -> Existing owner? -> Hold existing owner OR Select eligible rep -> Rep found? -> Prepare HubSpot writeback OR Hold for RevOps review

What you need before you start

Set the inputs and ownership rules before you connect live systems.

Inputs

  • hubspot_record_id (string, required) — The HubSpot contact or lead record to update.
  • email (email, required) — Used for contact and company enrichment.
  • company_domain (domain, optional) — Preferred company matching key when HubSpot already has it.
  • existing_owner_id (string, optional) — A non-empty value prevents automatic reassignment.
  • country (string, required) — ISO country code used to derive a routing region.
  • employee_count (integer, required) — Used to derive the commercial segment.
  • target_account (boolean, optional) — Can promote a lead into a named-account routing branch.
  • reps (array, required) — Rep roster containing owner IDs, territories, segments, availability, current load and capacity weight.

Expected outputs

  • assigned_owner_id (string, required) — HubSpot owner ID selected from the eligible reps view.
  • assigned_owner_email (email, required) — Human-readable assignment for review.
  • routing_reason (string, required) — The territory, segment and capacity rule that selected the rep.
  • routing_status (enum, required) — assigned, held_existing_owner, or held_no_eligible_rep.

Step 1

Receive the lead and current rep roster

Start one workflow run with the fields required to make a routing decision.

Configuration: {"tested_trigger": "manual", "production_options": ["webhook", "audience segment", "workflow routine"]}

Step 2

Normalize the lead and derive routing keys

Normalize identifiers, map country to territory and map headcount to segment.

Configuration: {"regions": ["North America", "UKI", "DACH", "Rest of Europe", "Unknown"], "segments": ["SMB", "Mid-market", "Enterprise", "Named account", "Unknown"]}

Step 3

Check whether HubSpot already has an owner

Stop before rep selection when a human or another system already owns the record.

Configuration: {"run_if": "existing_owner_id is not empty", "terminal_status": "held_existing_owner"}

Step 4

Preserve an existing owner

Return an auditable hold result without changing ownership.

Configuration: {"output": "held_existing_owner"}

Step 5

Select an eligible rep by capacity

Exclude inactive or out-of-office reps, require territory and segment matches, then choose the lowest current load divided by capacity weight.

Configuration: {"required": ["active = true", "out_of_office = false", "region match", "segment match", "hubspot_owner_id present"], "score": "current_open_leads / capacity_weight"}

Step 6

Check whether an eligible rep was found

Split successful assignments from records that need RevOps review.

Configuration: {"assigned_if": "routing_status equals assigned", "default": "held_no_eligible_rep"}

Step 7

Prepare the HubSpot owner writeback

Emit the exact record ID, owner ID, owner email and routing reason for a downstream HubSpot action.

Configuration: {"write_only_if": "existing_owner_id is blank AND assigned_owner_id is present", "fields": ["hubspot_record_id", "hubspot_owner_id", "routing_reason"]}

Step 8

Hold unmatched leads for RevOps review

Make empty eligible-rep pools visible instead of silently dropping or randomly assigning leads.

Configuration: {"status": "held_no_eligible_rep", "review_fields": ["hubspot_record_id", "routing_status", "routing_reason", "eligible_rep_count"]}

How to test the Clay workflow

We built this in Clay and ran it against 3 real scenarios to confirm it behaves exactly as described. Here’s what we checked, and what it took to get there.

  • Existing owner — confirmed working as expected.
  • Enterprise UK lead — confirmed working as expected.
  • No active territory owner — confirmed working as expected.

We last ran this in Clay on 2026-09-11, and every scenario worked as expected. It cost nothing to test — zero Clay credits used.

What we had to fix along the way

  • Clay code-node output schemas rejected array fields, so the rep roster is serialized as JSON between deterministic code steps.
  • The standalone code tester allowed Python datetime while the workflow runtime did not, so timestamps were moved out of the routing core.
  • Clay treated an empty optional mapped field as missing until the downstream input schema explicitly excluded it from the required list.

Deploy it to Clay in one command

Not a click-a-button-in-the-browser deploy — one command in a terminal, using Clay’s own official CLI. Download both files below into the same folder, then run:

python3 install_clay_workflow.py.txt hubspot-clay-lead-routing.clay-workflow.json --run-tests

That rebuilds every step of this workflow inside your own Clay workspace, as an unpublished draft. Nothing goes live and nothing touches your CRM until you review it and turn it on yourself.

Before you run it: you need Clay’s official CLI (Terracotta) installed and signed in to your workspace, plus Python 3 on your machine. If that's not your thing, our fractional GTM engineering team can deploy it for you.

Guardrails and limitations

Guardrails

  • Never overwrite a non-empty HubSpot owner automatically.
  • Re-run the tests after changing territory, segment or capacity rules.
  • Hold the row when no eligible rep exists; do not fall back to an arbitrary owner.
  • Keep API credentials in Clay connections, not in formula columns or downloaded files.
  • Write the assignment reason so RevOps can audit the rule later.
  • Add the live HubSpot write action only after mapping it against a test portal.

Current limitations

  • The downloadable workflow stops at a HubSpot-ready payload; it does not include a live HubSpot write action or credentials.
  • The rep roster must be supplied by the trigger or replaced with a governed CRM, warehouse or Clay function lookup.
  • Capacity-adjusted selection is deterministic for the supplied roster. True round-robin state across independent workflow runs needs an external state store.
  • Clay currently documents public template links for tables and workbooks, not Workflows. The provided installer recreates the workflow through official CLI commands instead.

Common questions

Can Clay route HubSpot leads without overwriting an existing owner?

Yes. Put an existing-owner conditional before rep selection and terminate that branch with a held_existing_owner status. The tested workflow preserved the supplied owner and never reached the assignment branch.

How does this Clay lead-routing workflow choose a rep?

It requires an active, available rep whose territory and segment match the lead, then selects the lowest current_open_leads divided by capacity_weight. Ties are resolved deterministically.

What happens when no HubSpot owner is eligible?

The workflow returns held_no_eligible_rep with an explanation and eligible_rep_count of zero. It does not fall back to a random owner.

Does the routing logic consume Clay credits?

The tested normalization, conditionals and Python code steps used zero data credits and zero action credits. A production HubSpot action or enrichment may have its own cost.

Is the download a native Clay workflow template?

No. Clay does not currently document public template links for Workflows. The download is a transparent bundle installed through Clay's official CLI; the installer recreates the draft, validates it and can run the tests again to confirm it still works.

Sources

Want a fractional GTM engineering team to build this in your real stack?

We map the rules, build the Clay workflow, connect your CRM, test every branch and leave your team with a system they can operate.

Explore the relevant service