Routing
Paths and conditions
Condition groups, operators, ownership rules and every field you can route on.
Every path in a router has a rule. When a lead arrives, Cauliflower checks the rules from top to bottom and runs the steps of the first path whose rule passes. This page covers how rules are built, every operator, ownership rules and every field you can route on.
Click a path node on the canvas to edit its rule. Under Rule type you choose between Without ownership, which matches on conditions alone, and CRM ownership, which also requires the lead to already have an owner.
Conditions and groups
A condition compares one field with a value, for example Employees greater or equal 1000. Conditions live in groups:
- Inside a group, the group's match setting decides whether all conditions must pass or any one of them is enough.
- When a path has more than one group, Groups match decides whether all groups must pass or any one of them.
Groups let you express "A and (B or C)". For an enterprise path that targets large companies in North America, either by headcount or by revenue:
| Group | Match | Conditions |
|---|---|---|
| Group 1 | all | Country is any of US, CA |
| Group 2 | any | Employees greater or equal 1000, Annual revenue (USD) greater or equal 100m |
With Groups match set to all, the path passes for a US company with 1,500 employees, or with 200 employees and 150 million in revenue, but not for a German company of any size.
A path with no conditions matches every lead ("No conditions: every lead matches this path"), and so does an empty group. A path can have up to 20 groups with up to 50 conditions each.
In the API, the same rule looks like this:
{
"type": "conditions",
"match": "all",
"groups": [
{ "id": "g1", "match": "all", "conditions": [
{ "id": "c1", "field": "country", "operator": "in", "value": "US, CA" }
] },
{ "id": "g2", "match": "any", "conditions": [
{ "id": "c2", "field": "enrichment.employees", "operator": "gte", "value": 1000 },
{ "id": "c3", "field": "enrichment.revenue", "operator": "gte", "value": "100m" }
] }
]
}Operators
| Operator | API name | Passes when |
|---|---|---|
| is | equals | The value equals the field. Numbers compare as numbers. For list fields, any item equals the value. |
| is not | not_equals | The opposite of is. |
| contains | contains | The field is not empty and contains the text. |
| does not contain | not_contains | The field doesn't contain the text. Empty fields pass. |
| starts with | starts_with | The field is not empty and starts with the text. |
| ends with | ends_with | The field is not empty and ends with the text, for example .edu. |
| is any of | in | The field equals one of the listed values. |
| is none of | not_in | The field equals none of the listed values. Empty fields pass. |
| greater than | gt | Both sides are numbers and the field is larger. |
| greater or equal | gte | Both sides are numbers and the field is larger or equal. |
| less than | lt | Both sides are numbers and the field is smaller. |
| less or equal | lte | Both sides are numbers and the field is smaller or equal. |
| is empty | is_empty | The field is missing, blank or an empty list. |
| is not empty | is_not_empty | The field has a value. |
| is true | is_true | The field is true, yes, y, 1, on, checked or a non-zero number. |
| is false | is_false | The field is anything else, including missing. |
| matches regex | matches | The field matches a regular expression (case-insensitive). |
The builder offers the operators that fit the field: yes/no fields get is true, is false, is empty and is not empty, and number fields get the comparisons. Fields you type by hand get every operator.
How values are compared
- Text comparisons ignore case and surrounding spaces, so
Acmeisacme. - Lists for is any of and is none of are separated by commas or new lines:
US, CA, MX. - Numbers tolerate formatting. Commas, spaces, currency signs and a trailing
+are ignored,k,mandbmultiply by a thousand, a million and a billion, and ranges compare by their lower bound. So10kis 10,000,$5Mis 5,000,000,1000+is 1,000 and the form answer51-200counts as 51. - Number comparisons never pass when either side isn't a number. A lead with no employee count fails both "greater or equal 1000" and "less than 1000", so add a path or catch-all for unknown sizes.
- List fields such as
enrichment.technologiespass is and is any of when any item matches, and contains checks the items joined into one text. - Regular expressions must be shorter than 500 characters. An invalid pattern never matches. For example,
^(ceo|cto|cfo|founder)on the job title.
Ownership rules
Set Rule type to CRM ownership to match leads whose account already has an owner. Choose where the owner comes from:
| Option | Owner checked |
|---|---|
| Owner in the CRM or a previous Cauliflower owner | The CRM record owner, then the lead's owner in Cauliflower |
| Owner in the CRM | crm.owner_email only |
| Previous Cauliflower owner | lead.owner_email only |
The CRM owner is read from the record you choose under Integrations (by default, the company's owner; in Attio, the attribute you pick). Conditions on an ownership path are optional and must pass as well. For example, you can match only owned accounts that are on an enterprise plan.
An ownership rule only checks that an owner email exists. Pair it with a Display calendar or Assign to step that targets the Record owner, which looks the owner up among your members and falls back to a team or person when they aren't one. See Path steps. In the preview, ownership paths show the owner that was found, or "not found".
Fields you can route on
The field picker groups fields into Form fields, Lead, Lead history, CRM and Enrichment. To use a field that isn't listed, type its key and choose Use field.
Lead fields
| Field | Label | Value |
|---|---|---|
email | The lead's email, lowercased | |
first_name, last_name, name | First name, Last name, Full name | Split from or joined with the full name when only one is sent |
company | Company | From the form, or the enriched company name when the form has none |
phone | Phone | From the form |
domain | Email domain | The part after the @ |
is_personal_email | Uses a personal email | True for Gmail, Outlook, iCloud, Proton and other free providers, plus the domains under Settings → Extra personal email domains |
country | Country | From a form field named country, country_code or region |
utm_source, utm_medium, utm_campaign | UTM source, medium, campaign | From the page URL, the widget or the API. Any other utm_ parameter works too. |
page_url | Page URL | The page the form was submitted from |
Lead history
| Field | Label | Value |
|---|---|---|
lead.is_existing | Lead seen before | A lead with this email already exists in the workspace |
lead.owner_email | Lead owner email | The email of the lead's current owner in Cauliflower |
lead.meetings_count | Lead meetings booked | Confirmed and completed meetings for this lead |
CRM fields
When Match against your CRM is on and a CRM is connected, the lookup adds:
| Field | Label | Value |
|---|---|---|
crm.person.exists | CRM · person exists | The CRM has a person (contact or lead) with this email |
crm.company.exists | CRM · company exists | The CRM has a company (account) for the email domain, or linked to the person |
crm.owner_email | CRM · owner email | The record owner's email |
Every attribute of the CRM records is available as well, as crm.person. or crm.company. followed by the attribute or property name, for example crm.company.industry or crm.person.job_title. The picker suggests attributes it has seen in this router's recent routing decisions. When the lookup is off, the two exists fields are false.
Enrichment fields
Normalized data from your CRM or Apollo. Lead enrichment explains where each value comes from.
| Field | Label | Type |
|---|---|---|
enrichment.found | Enrichment found | yes/no |
enrichment.source | Enrichment source (attio / apollo) | text |
enrichment.title | Job title | text |
enrichment.seniority | Seniority | text |
enrichment.department | Department | text |
enrichment.country | Person country | text |
enrichment.city | Person city | text |
enrichment.linkedin_url | LinkedIn URL | text |
enrichment.company_name | Company name | text |
enrichment.company_domain | Company domain | text |
enrichment.industry | Industry | text |
enrichment.employees | Employees | number |
enrichment.revenue | Annual revenue (USD) | number |
enrichment.company_country | Company country | text |
enrichment.founded_year | Founded year | number |
enrichment.technologies | Technologies | list |
enrichment.state (the person's state or region, from Apollo) is also available if you type it.
Form fields and custom fields
Every field declared on the router's trigger appears under Form fields, with its options when it's a select. Anything else the form, the widget or the API sends can be routed on by typing its key. Keys ignore case, and spaces and dashes count as underscores, so Company Size and company_size are the same field.
Rules for common cases
| Path | Rule |
|---|---|
| Personal email | Uses a personal email is true |
| Students | Email ends with .edu |
| Enterprise | Employees greater or equal 1000 |
| Existing customers | CRM ownership, owner in the CRM |
| Paid search | UTM source is any of google, bing and UTM medium is cpc |
| Returning leads | Lead seen before is true and Lead meetings booked greater than 0 |
Use the live preview to check each path: it lists every condition with the value it expected and the value it found.