# Lead Triage Router

> Scores inbound leads with a tunable ICP-fit and intent rubric, assigns a tier and a routing owner from your territory rules, and prints the exact signals behind every score so reps can see why a lead reached them.

Lead Triage Router is an AI skill that you can use in Claude Code, Codex, or type.com.

- Skill page: https://typestaging.com/library/skills/lead-triage-router
- Raw SKILL.md: https://typestaging.com/library/skills/lead-triage-router/SKILL.md
- Created by the type.com team
- Updated: September 24, 2026
- Package: 8 files

## How to use Lead Triage Router

1. **type.com:** Choose Use Skill to add Lead Triage Router to your type.com workspace.
2. **Claude Code:** Download the skill and extract it into ~/.claude/skills/lead-triage-router/ (or .claude/skills/lead-triage-router/ in a project). Then ask Claude to use Lead Triage Router.
3. **Codex:** Download the skill and extract it into ~/.agents/skills/lead-triage-router/ (or .agents/skills/lead-triage-router/ in a repository). Then ask Codex to use Lead Triage Router.

## Frequently asked questions

### What is Lead Triage Router?

Lead Triage Router is an AI skill that you can use in Claude Code, Codex, or type.com.

### What does Lead Triage Router do?

Scores inbound leads with a tunable ICP-fit and intent rubric, assigns a tier and a routing owner from your territory rules, and prints the exact signals behind every score so reps can see why a lead reached them.

### Can I use Lead Triage Router in Claude Code or Codex?

Yes. Lead Triage Router is a standard Agent Skills folder with a SKILL.md file. Download it and extract it into ~/.claude/skills/ for Claude Code or ~/.agents/skills/ for Codex.

### Who created Lead Triage Router?

The type.com team created Lead Triage Router. The type.com team reviews every skill before it is published in the Skills Library.

## Example output

Example output from a sample run. Company names and figures are sample data.

```markdown
## Lead triage and routing: 19 inbound leads, as of 2026-09-22

**5 Tier A leads are sales-ready now, led by Derek Nwosu at Kestrel Payments (94.0), routed to Alex Rivera on a 2h SLA.** The rest split into 3 Tier B, 7 Tier C, 1 Tier D and 3 disqualified, including a 69.2 founder lead below the 10-employee floor.

**Tier A and B leads**

| Tier | Score | Lead | Fit | Intent | Route (SLA) |
| --- | ---: | --- | ---: | ---: | --- |
| A | 94.0 | Derek Nwosu, Kestrel Payments | 90.0 | 100.0 | enterprise-na / Alex Rivera (2h) |
| A | 86.0 | Priya Shah, Northwind Logistics | 90.0 | 80.0 | enterprise-na / Alex Rivera (2h) |
| A | 83.8 | Paulo Mendes, Vertex Freight | 89.0 | 76.0 | enterprise-apac / Wei Chen (4h) |
| A | 83.2 | Ken Arai, Meridian Manufacturing | 88.0 | 76.0 | enterprise-apac / Wei Chen (4h) |
| A | 72.7 | Marcus Feld, Atlas Bio Labs | 86.0 | 52.7 | enterprise-emea / Ingrid Solberg (4h) |
| B | 67.8 | Amara Osei, Cascade Fintech | 81.0 | 48.0 | sdr-outbound / SDR pod 1 (12h) |
| B | 57.6 | Mira Lindqvist, Lantern Bay Hotels | 52.0 | 66.0 | sdr-outbound / SDR pod 1 (12h) |
| B | 52.8 | Robert Kline, Silverline Energy | 88.0 | 0.0 | enterprise-na / Alex Rivera (8h) |

…plus 7 Tier C, 1 Tier D and 3 disqualified leads in the full report. Scoring: total = 0.60 x fit + 0.40 x (intent x recency); intent shown after recency.

**Why Marcus Feld scored 72.7:** fit 86.0 from 3,200 employees +30, revenue $900,000,000 +20, Healthcare +14, Germany +8 and a director title +14. Intent 62.0 from a started trial +30, pricing page views +8, 2 content downloads +8, 4 sessions +8 and a webinar +8, discounted x0.85 because his last activity was 21d ago, giving 52.7.

**Routing load**

| Queue | Owner | Leads |
| --- | --- | ---: |
| sdr-sequence | SDR pod 2 | 7 |
| marketing-nurture | (none) | 4 |
| enterprise-na | Alex Rivera | 3 |
| enterprise-apac | Wei Chen | 2 |
| sdr-outbound | SDR pod 1 | 2 |
| enterprise-emea | Ingrid Solberg | 1 |

**Next actions**
- Work Tier A immediately; qualify Tier B before routing to an AE; sequence Tier C, do not call; Tier D is marketing nurture only.
- Robert Kline (Silverline Energy, referral) has fit 88.0 but no activity date on record, so intent scored 0.0.
- Disqualified to marketing-nurture: Sana Iqbal, Tiny Tools Co (4 employees is below the 10 floor); Bill Hartley, Peak Staff Group (blocked industry: Staffing); Jen Larkin, Solstice Apparel Supply (personal email domain gmail.com).
- Hana Kovacs (Brightwater Utilities) is missing employee_count and annual_revenue_usd; missing fields score zero rather than guessing.

**Inputs:** an inbound lead export, territory routing rules and the scoring rubric in config/rubric.json.

_Produced by running `bash examples/run.sh` on the sample data bundled with this skill._
```

## Package files

- DATA_CONTRACT.md
- SKILL.md
- config/rubric.json
- examples/expected_output.txt
- examples/run.sh
- examples/sample_leads.csv
- examples/sample_routing.csv
- scripts/lead_triage.py

## SKILL.md

````markdown
---
name: lead-triage-router
description: Scores inbound leads with a tunable ICP-fit and intent rubric, assigns a tier and a routing owner from your territory rules, and prints the exact signals behind every score so reps can see why a lead reached them.
---

# Lead Triage and Router

Takes a raw inbound lead export and produces a tiered, routed, explainable
work list. The rubric lives in `config/rubric.json`, not in the code, so
marketing and sales can argue about weights in a pull request instead of in a
meeting.

## Before you run

This skill ships scripts and sample data alongside this SKILL.md. Before running any command:

1. **Get the files.** Make sure the skill's other files (`scripts/`, `examples/` and anything else listed with this skill) are in your working folder at the same relative paths. Some environments load only SKILL.md; if yours did, fetch each file from this skill's published files and write it to the matching path. In Type, read them with the skill-file tools. Anywhere else, the Type Skills Library API lists every file with its path, content and `sha256`: GET `https://api.type.com/api/public/library/skills` and take the entry with slug `lead-triage-router`.
2. **Check the copies are exact.** Compare each file's size in bytes, not characters (and its hash, where your tools report one), with the published version before running. A copy written out from the published file is fine once its byte size and hash match; never run a script you summarised or reconstructed from memory.
3. **Run from the skill's folder**, calling interpreters explicitly: `python3 scripts/…` and `bash examples/run.sh`.
4. **Try the sample first.** If the skill ships `examples/run.sh` and `examples/expected_output.txt`, run `bash examples/run.sh`; its output should match the expected file exactly. If it doesn't, stop and report the first differing line rather than running on real data.

## When to use this

- Daily or hourly triage of form fills, trials, and content downloads when the
  volume has outgrown a human reading each one.
- Rebuilding lead scoring after an ICP change, a new segment, or a territory
  reshuffle — run the new rubric against last month's leads and compare.
- Auditing an existing scoring model: this shows its reasoning, whereas most
  CRM scoring does not.

## Inputs

`DATA_CONTRACT.md` has the exact columns, types, and units. You need:

1. `leads.csv` — one row per lead, firmographics plus intent counters.
2. `routing.csv` — the territory/queue rules, in priority order.
3. `config/rubric.json` — weights, bands, tiers, disqualifiers (ships with
   sensible B2B SaaS defaults).

### Gathering the inputs through Type integrations

- **HubSpot**: export contacts with `email`, `jobtitle`, `company`,
  `num_employees` (or the enriched company property), `annualrevenue`,
  `industry`, `country`, `hs_analytics_source`, `createdate`, and
  `notes_last_contacted`. Page-view and session counters come from the HubSpot
  analytics properties or from your product analytics tool.
- **Salesforce**: `Lead` object plus any enrichment fields (Clearbit, ZoomInfo,
  Apollo) that populate employee count, revenue, and industry.
- **Attio**: the People object joined to the linked Company record.
- **Product analytics (Amplitude, PostHog, GA4, warehouse)**: pricing page
  views, sessions, trial starts. Aggregate to a 30-day window ending on the
  date you pass to `--as-of` so the counters and the recency multiplier agree.
- **Routing rules**: usually a spreadsheet RevOps already maintains. If not,
  write it once by hand from the contract; it changes rarely.

If an enrichment field is missing, leave it blank. Blank fields score zero and
are listed in the data-quality section — do not guess values, because a guessed
employee count silently inflates a tier.

## Running it

```bash
python3 scripts/lead_triage.py \
  --leads path/to/leads.csv \
  --routing path/to/routing.csv \
  --config config/rubric.json \
  --as-of 2026-09-22
```

- `--as-of` is required and drives the intent recency multiplier.
- `--fallback-queue NAME` sets where leads go when no routing rule matches
  (default `unrouted`); those leads are counted separately in the summary.
- `--json` emits the full scored record, contribution by contribution.
- Bad input exits `2` naming the file, row, and column.

See `examples/run.sh` for a complete working invocation.

## How the score works

```
total = fit_weight x fit_score + intent_weight x (intent_score x recency_multiplier)
```

- **Fit (0-100)** sums five firmographic bands — employee count, revenue,
  industry, country, and title seniority — then normalizes against
  `fit.max_points`. Title matching is whole-word, so "Director" does not match
  the executive keyword list, and a multi-word keyword such as "head of" must
  match consecutive whole words, so "Forehead Office Manager" scores as a
  manager, not a head of function. An industry that is listed but scores below
  the `_default` is labelled as low-fit, not as an ICP industry.
- **Intent (0-100)** sums weighted behavioural signals. Count signals have a
  per-signal cap (three pricing-page views should not outscore a demo request),
  and the total is capped at `intent.max_points`.
- **Recency multiplier** discounts intent by how long ago the lead last did
  anything. A lead with no activity date is discounted to the floor rather than
  treated as fresh.
- **Disqualifiers** run before tiering. A personal email domain, a blocked
  industry or country, or a company below the employee floor sends the lead to
  the nurture queue with the reason attached, whatever it scored.
- **Tier** is the highest band whose `min_score` the total meets.
- **Routing** walks the rules in `priority` then `rule_id` order and takes the
  first match on tier, segment, and region. `*` is a wildcard; keep one
  catch-all rule at the bottom.

## Tuning

Edit `config/rubric.json`:

- `fit_weight` / `intent_weight` must sum to 1.0. Product-led motions usually
  run 0.4 / 0.6; enterprise outbound-heavy motions run 0.7 / 0.3.
- `fit.*_bands`, `industry_points`, `country_points`, `title_bands` — the ICP
  itself. Points are arbitrary units; only their ratio matters.
- `intent.signals` — add a field here and the contract, and it is scored. Caps
  stop one loud signal from dominating.
- `tiers[].min_score` — move these first when the A tier is too big for the
  team to work inside the SLA.
- `disqualifiers` — the hard filters, kept separate from scoring on purpose.

After any change, rerun against a week of known leads and check the tier counts
against what the team can actually handle.

## Reading and presenting the output

- **Summary** gives the tier distribution and average score. If Tier A is more
  than about 15% of volume, thresholds are too loose.
- **Routing load** is the operational number: how many leads each queue and rep
  just received. Check it against capacity before you send the assignments.
- **Per-lead blocks** are the deliverable. The `fit` and `intent` lines
  reconstruct the score exactly — paste them into the CRM note or the Slack
  handoff so the rep never has to ask "why did I get this?"
- **Data quality** tells you what enrichment is missing. Persistent blanks
  there are a better investment than another weight change.

## Limits

- It scores the *record*, not the person's real buying intent. A rubric cannot
  see a stalled budget cycle or a competitor already in the account.
- Keyword title matching is coarse. Unusual or non-English titles land in the
  default band; add keywords rather than accepting the drift.
- Counts are taken as given. If the 30-day window in the export does not end on
  `--as-of`, the recency multiplier and the counters disagree and scores skew.
- No deduplication: two form fills from the same person are two leads. Dedupe
  in the export, or in the CRM, first.
- No write-back. It does not assign owners in the CRM, send notifications, or
  enroll anyone in a sequence.
- Disqualifiers are rules, not judgment. A 4-person company with a real budget
  still gets filtered; review the DQ list weekly rather than trusting it blindly.
````
