> ## Documentation Index
> Fetch the complete documentation index at: https://80x.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# AI ICP-fit research and routing

> High-intent inbound leads get researched, scored against your ideal customer profile, and routed to a rep only when they clear the bar.

High-intent inbound leads get researched, scored against your ideal customer profile, and routed to a rep only when they clear the bar. A high-intent lead is one that shows real buying interest, such as a demo request or a pricing-page visit. This recipe fires on [Record added to list](/reference/triggers/record-added-to-list). It then runs a read-only [Web agent](/reference/agents/web-agent) behind a free [Filter](/reference/conditions/filter). Research only runs on companies that clear your ideal customer profile (ICP) threshold. Your ICP is the kind of company you most want to sell to. From there, [Classify record](/reference/ai/classify-record) assigns an A, B, or C fit band. [Switch](/reference/conditions/switch) branches on that band, and [Update record](/reference/records/update-record) saves the research. The recipe then assigns qualified leads with [Round robin](/reference/workspace/round-robin) and notifies the owner with a [Slack](/reference/integrations) message.

| | |
| - | - |
| **Difficulty** | Advanced |
| **Time to build** | \~25 minutes |
| **Trigger** | [Record added to list](/reference/triggers/record-added-to-list), or [Webhook received](/reference/triggers/webhook-received) |
| **Blocks used** | [Filter](/reference/conditions/filter), [Web agent](/reference/agents/web-agent), [Classify record](/reference/ai/classify-record), [Switch](/reference/conditions/switch), [Update record](/reference/records/update-record), [Round robin](/reference/workspace/round-robin), [Slack](/reference/integrations) |
| **Credit cost per run** | Variable. The Web agent is token-based, and each write costs 1 credit. See [Credits & pricing explained](/reference/credits-and-pricing) |
| **Plan required** | Pro recommended |

## Prerequisites

* An Attio plan that includes the agent and AI blocks. Pro is recommended for the volume this recipe assumes. Confirm current plan availability on [Credits & pricing explained](/reference/credits-and-pricing).
* The Slack integration connected to your workspace, with the workflow authorized to post in your routing channel.
* A list for high-intent inbound leads. For example, a list named `Inbound: high intent` that a form, a product event, or a routing rule adds leads to.
* Three attributes on the Companies object. Add a long-text attribute for the research summary (slug `icp_research`). Add a single-select attribute for the fit band (slug `fit_band`, with options `A`, `B`, and `C`). Add an owner attribute (slug `owner`).
* A read-only **Run as** identity for the Web agent. The agent reads records and the public web but cannot write back. See [Permissions & workflow access](/explanation/permissions-and-access).

## How it works

The trigger fires the moment a lead enters the high-intent list. A free Filter checks the lead against your ICP threshold and stops the run when it does not qualify. This means the token-priced Web agent never touches raw, low-fit traffic. Each qualifying company reaches the Web agent, which answers two to four specific qualification questions read-only.

Classify record turns that research into an A, B, or C fit band. A Switch then branches on the band. Update record saves the summary and band to the company. Round robin assigns an owner to A and B leads, and a Slack message notifies that owner.

The blocks run in this order:

1. [Record added to list](/reference/triggers/record-added-to-list): a high-intent lead enters the list.
2. [Filter](/reference/conditions/filter): continue only when the lead clears the ICP threshold. The run stops otherwise.
3. [Web agent](/reference/agents/web-agent): research the company read-only against two to four qualification questions.
4. [Classify record](/reference/ai/classify-record): assign an A, B, or C fit band from the research.
5. [Switch](/reference/conditions/switch): branch on the fit band.
6. [Update record](/reference/records/update-record): save the research summary and band to the company.
7. [Round robin](/reference/workspace/round-robin): assign an owner to A and B leads only.
8. [Slack](/reference/integrations): notify the assigned owner.

## Build steps

<Steps>
  <Step title="Add the trigger">
    Create a new workflow. Click the **Add trigger** button, choose [Record added to list](/reference/triggers/record-added-to-list), and select your high-intent inbound list. If your leads arrive from an external form or product event, use [Webhook received](/reference/triggers/webhook-received) instead. In that case, resolve the payload to a company record before the Filter.

    | Field | Value |
    | - | - |
    | Trigger | Record added to list |
    | List | `Inbound: high intent` |
    | Object | Companies |
  </Step>

  <Step title="Gate the agent with a Filter">
    Add a [Filter](/reference/conditions/filter) immediately after the trigger. The Filter is the cost guard. It is free, and it stops every below-threshold run before the Web agent spends a token. Set conditions that approximate your ICP.

    | Field | Value |
    | - | - |
    | Block | Filter |
    | Condition 1 | `domain` is not empty |
    | Condition 2 | `employee_count` is greater than 50 |
    | Condition 3 | `region` is one of EMEA, North America |
    | Match | All conditions |

    <Warning>
      Place the Filter before the Web agent, not after. The Web agent is token-priced, so a Filter that sits downstream spends credits on leads you intended to skip.
    </Warning>
  </Step>

  <Step title="Research with a read-only Web agent">
    Add a [Web agent](/reference/agents/web-agent) after the Filter. Set its **Run as** identity to a read-only research user. The agent then reads the record and the public web but cannot write back. Keep the prompt to two to four specific questions.

    | Field | Value |
    | - | - |
    | Block | Web agent |
    | Run as | Research (read-only) |
    | Input | `acme.com` (from `domain`) |
    | Questions | What does this company sell? Who is the likely buyer? Why might they need our product now? What is a relevant first line for outreach? |

    Use a tightly scoped prompt so the agent returns structured, verifiable research:

    ```text theme={null}
    Research this company for a B2B sales rep. Return:
    1. one-sentence company summary
    2. likely business model
    3. why they may have urgency now
    4. one concise outreach angle

    Avoid unsupported claims. If you cannot verify something, say so.
    ```
  </Step>

  <Step title="Classify into fit bands">
    Add a [Classify record](/reference/ai/classify-record) block. Feed it the company record plus the agent's research, and have it return a single fit band.

    | Field | Value |
    | - | - |
    | Block | Classify record |
    | Input | `{{ web_agent.summary }}` plus company attributes |
    | Categories | A is strong ICP fit; B is partial fit; C is weak or out of ICP |
    | Output | `fit_band` |
  </Step>

  <Step title="Branch on the band with a Switch">
    Add a [Switch](/reference/conditions/switch) on the fit band so A and B leads get routed and C leads are saved but not assigned.

    | Field | Value |
    | - | - |
    | Block | Switch |
    | Switch on | `{{ classify_record.fit_band }}` |
    | Case A | Save, assign, notify |
    | Case B | Save, assign, notify |
    | Case C | Save only |
  </Step>

  <Step title="Save the research">
    Add an [Update record](/reference/records/update-record) block in every branch to write the research and band back to the company. The Web agent does not save anything on its own.

    | Field | Value |
    | - | - |
    | Block | Update record |
    | Record | Trigger company |
    | `icp_research` | `{{ web_agent.summary }}` |
    | `fit_band` | `{{ classify_record.fit_band }}` |
  </Step>

  <Step title="Assign qualified leads with Round robin">
    Add a [Round robin](/reference/workspace/round-robin) block in the A and B branches only. It picks the next rep and writes the owner.

    | Field | Value |
    | - | - |
    | Block | Round robin |
    | Pool | EMEA AEs: Priya Sharma, Jordan Lee, Mateo Ruiz |
    | Assign to | `owner` |
  </Step>

  <Step title="Notify the owner over Slack">
    Add a [Slack](/reference/integrations) block in the A and B branches to message the assigned rep.

    | Field | Value |
    | - | - |
    | Block | Slack |
    | Channel or user | Assigned owner from Round robin |
    | Message | `New {{ fit_band }} lead: {{ company.name }} ({{ domain }}). Research saved in Attio. Owner: {{ owner }}.` |
  </Step>
</Steps>

## Variable mapping

| Variable | Source block | Field | Example value |
| - | - | - | - |
| `domain` | Trigger | Company `domain` | `acme.com` |
| `web_agent.summary` | Web agent | Research output | "Acme sells warehouse robotics to mid-market 3PLs..." |
| `classify_record.fit_band` | Classify record | Category | `A` |
| `owner` | Round robin | Assigned rep | Priya Sharma |
| `company.name` | Trigger | Company `name` | Acme Robotics |

## Credit cost worked example

Assume 400 leads enter the high-intent list each month, and the ICP Filter passes about 30% of them (120 leads). The Filter, Switch, and Round robin are free logic blocks. The token-based cost of this recipe is the Web agent, plus Classify record and the writes.

* **Filter**: free, runs on all 400 leads.
* **Web agent**: variable and token-based. It runs only on the \~120 leads that pass the Filter. Do not assume a fixed number. Sample about 10 representative leads, run them, and read the actual per-run credit cost in the run viewer before you scale. Gating with the Filter is what keeps this line from running on all 400.
* **Classify record**: variable and token-based, about 120 runs. Sample and read the per-run cost the same way.
* **Update record**: 1 credit each, about 120 runs.
* **Round robin**: free, A and B leads only.
* **Slack**: 1 credit each, A and B leads only.

So the fixed-rate floor is roughly 120 Update writes plus one Slack message per assigned A or B lead. The variable ceiling is the Web agent and Classify record totals you measure in the run viewer. Compare that monthly total against your plan allowance on [Credits & pricing explained](/reference/credits-and-pricing).

<Note>
  Per-workflow credit caps were removed in the new engine, so the Filter is your spend control. There is no cap to fall back on. The tighter the Filter, the fewer leads reach the token-based blocks.
</Note>

## Test plan

1. Add a real ICP-fit company to the high-intent list to fire the trigger. For example, use a 200-person EMEA SaaS firm with a populated `domain`.
2. Open the run in run history and confirm the **Filter** passed. Expect a green continue.
3. Confirm the **Web agent** returned a research summary keyed to your four questions, and that its **Run as** identity is read-only.
4. Confirm **Classify record** output an `A`, `B`, or `C` band.
5. Confirm the **Switch** entered the matching branch, and that **Update record** wrote `icp_research` and `fit_band` to the company. For A and B, also confirm that **Round robin** set an owner and **Slack** posted to that owner.
6. Now add a deliberately failing input: a lead with an empty `domain` and an `employee_count` of 5. Expect the **Filter** to stop the run before the Web agent. Verify in run history that no agent or write block executed and that no tokens were spent.

## Failure modes

| Symptom | Cause | Fix |
| - | - | - |
| Credit spend far higher than expected | The Web agent runs on raw, low-fit leads | Tighten the Filter, confirm it sits before the Web agent, and re-sample 10 runs in the run viewer. |
| The research summary is blank in Attio | No Update record follows the agent | Agent output is not saved automatically. Add Update record mapping `{{ web_agent.summary }}` into `icp_research`. |
| The agent attempts to write, or errors on permissions | The **Run as** identity has write access or is the wrong user | Set the Web agent's **Run as** to a read-only research user. |
| C leads get assigned and pinged | Round robin and Slack sit outside the Switch | Move Round robin and Slack into the A and B branches only. |
| The owner field stays unset on qualified leads | The Round robin pool is empty or not mapped to `owner` | Populate the pool and map the assigned rep to the `owner` attribute. |
| Webhook leads never trigger | The payload is not resolved to a company record | When using Webhook received, create or match the company before the Filter. |

## Variations

### Command trigger to control spend

Swap [Record added to list](/reference/triggers/record-added-to-list) for [Record command](/reference/triggers/record-command) so a human chooses when a lead is worth researching. Everything downstream stays identical. This puts the token-priced Web agent behind an explicit click instead of an automatic list event. That is the safest way to cap spend while you tune the Filter.

### Summarize record for a cheaper tier

For high volume, or for B-tier leads where public-web research is not worth the cost, replace the [Web agent](/reference/agents/web-agent) with [Summarize record](/reference/ai/summarize-record). It is still token-based, but it works only from data already in Attio with no web browsing. That means it costs less per run. Keep the Filter, Classify record, Switch, and routing unchanged. Only the research block changes.

## See also

* [AI agents playbook](/guides/signals-and-ai/ai-agents-playbook)
* [Permissions & workflow access](/explanation/permissions-and-access)
* [Web agent](/reference/agents/web-agent)
* [Classify record](/reference/ai/classify-record)
* [Credits & pricing explained](/reference/credits-and-pricing)

***

*Sources: [Attio Workflows block library](https://attio.com/help/reference/automations/workflows/workflows-block-library), [Manage workspace and seat credits](https://attio.com/help/reference/workspace-settings-billing/manage-workspace-and-seat-credits). Last reviewed against the Attio Help Center, June 2026.*


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.