Custom fields
LeadHunter ships with the obvious fields — name, website, phone, address, status — but every business tracks attributes specific to its market. Custom fields let you define those once, attach them to every account in your company, and reference them everywhere you can reference a built-in field: account forms, saved filters, campaign bulk-add, the scoring prompt.
Custom fields are scoped to a Company — a bike-shop CRM’s fleet_size is invisible to a podcast directory in another Company; neither company’s schema pollutes the other’s data.
Field types
Section titled “Field types”| Type | What it stores | Example |
|---|---|---|
text | Short single-line string | License number |
textarea | Long multi-line string | Notes about the location |
number | Integer or decimal | Number of franchises |
date | YYYY-MM-DD | Contract renewal date |
boolean | true / false | Has loyalty program? |
select | One value from a list | Tier: Gold / Silver / Bronze |
multiselect | Many values from a list | Languages spoken |
url | Validated URL | Reviews page |
Picking the right type
Section titled “Picking the right type”A quick decision guide for which type to reach for:
| You want to capture… | Use |
|---|---|
| One label from a finite list (Gold / Silver / Bronze) | select |
| Many labels from a finite list (multi-tag) | multiselect |
| A yes/no flag | boolean |
| A count you’ll want to filter or sort by | number |
| A specific date | date |
| A clickable URL | url |
| A short opaque string (license number, account ref) | text |
| A long free-form blob | textarea (but consider: would notes on the Account itself do?) |
Picking the most-restrictive type that fits makes the field usable in saved filters with the right operators. text is the catch-all, but a select would let you filter cleanly while text only supports substring matching.
Anatomy of a definition
Section titled “Anatomy of a definition”From Settings → Custom fields → Add field, each definition carries:
| Property | What it does |
|---|---|
| Key | Machine name. Lowercase, snake_case, starts with a letter (regex ^[a-z][a-z0-9_]{0,79}$). Shows up in filter expressions as cf.<key>. |
| Label | Human-readable name shown on account forms (e.g. “Fleet size”). |
| Type | One of the eight above. |
| Options | Required for select / multiselect. The exact strings counted as valid values. |
| Help text | One-line hint shown next to the input on the account form. |
| Required | When on, account create / edit forms reject submissions that don’t fill this field. Partial updates skip the check, so a re-import that omits the field doesn’t fail. |
| Order | Display position on account forms. Lower numbers appear first. |
The schema is per-Company, not global. A field defined under Bike Shop CRM doesn’t show up under Radio Stations — each tenant’s fields are isolated.
How values land on accounts
Section titled “How values land on accounts”Two paths an operator can use:
- The account form — every defined field shows up in the Custom fields section of the new-account and edit-account screens, in the order you set. Validates on save against the declared type.
- CSV / XLSX import — map a column to a custom field key in the import wizard’s column-mapping step. The static-values trick from Import accounts also works here: set a batch-wide custom-field value (e.g. every row in this file is
cf.source_list = "Trade show 2026") without needing a column for it.
Auto-enrichment doesn’t fill custom fields. The discover-website + scrape + language-detect pipeline only populates the standard fields (
website,website_content,language). If you need a custom field populated automatically, fill it on import or on the account form.
Custom fields feed scoring
Section titled “Custom fields feed scoring”Every value you put in a custom field is part of the prompt the scoring model reads when ranking the account against the Product’s ICP. So a thoughtfully-defined custom field is one of the easiest ways to inject business context into scoring without re-writing the ICP.
A bike-shop CRM with cf.fleet_size, cf.brands_carried, and cf.service_offered on every row gives the model concrete numerical and categorical signals it can use directly. The same accounts without those custom fields force the model to guess from the website alone.
This is the difference between custom fields and the notes field on the Account, which is operator-only and isn’t read by the scoring prompt. See Account → Notes are operator-only.
Filtering and segmentation
Section titled “Filtering and segmentation”Reference custom fields in saved filters with the cf. prefix:
match: allconditions: country = Spain cf.tier in [Gold, Platinum] cf.contract_renewal_date lt 2026-06-30Custom fields support the same operators as built-ins, modulo the field type — gt / lt apply only to numbers and dates; is_true / is_false only to booleans; contains differs between text (substring) and multiselect (array membership). See Build a saved filter for the full operator matrix and worked examples.
How custom fields interact with merges
Section titled “How custom fields interact with merges”When two accounts merge, custom fields are unioned:
text/textarea/number/date/url/select/boolean— if only one row has a value, that value sticks; if both have values, the survivor’s wins but the absorbed row’s value is recorded inmerge_historyso you can read what was overridden.multiselect— union of both arrays. No loss.
Nothing is silently dropped, even on a conflict.
Adding, renaming, deleting
Section titled “Adding, renaming, deleting”| Action | Effect |
|---|---|
| Add a field | Instant. Existing accounts immediately gain the field with a null value; new accounts pick it up on the next form load. |
| Rename label or help text | Instant. Filters and forms keep working — they reference the key, not the label. |
| Rename a key | Not safe. References in saved filters and import mappings break silently. Prefer creating a new field with the new key, backfilling values, then deleting the old one. |
| Delete a field | Removes it from the schema (stops showing in forms; rejected by the import wizard). The values stay on existing accounts in the underlying JSON blob — they just become invisible. Cheap to re-add; expensive to recover deleted history. If you’re sure you want the values gone too, ask support for a one-off cleanup. |
What’s not a good fit for custom fields
Section titled “What’s not a good fit for custom fields”- Personal data on individual people — names, emails, phone numbers of specific contacts. Those belong on the Contact model, not on the Account’s custom fields.
- High-cardinality free text you never plan to filter on. A 10KB description of every account belongs in
notes, not in atextareacustom field — custom fields are meant to be queryable, and JSON-indexed long text doesn’t query efficiently. - Anything that’s the same for every account — that’s a company-level setting, not a field on each row.
- Anything you’ll want to aggregate across the whole database — define a custom field that filters cleanly (number, date, select) and use saved filters for the aggregation; the saved-filter reach estimator is the closest thing to a count-by query in LeadHunter today.
Worked example 1 — a bike-shop CRM
Section titled “Worked example 1 — a bike-shop CRM”For a Company running outbound to bike shops, useful custom fields might be:
| Field | Type | Why |
|---|---|---|
fleet_size | number | Target by store size. “Shops with ≥20 bikes in stock” is a meaningful audience. |
brands_carried | multiselect, options = the brands you care about | ”Shops that carry Trek but not Specialized” is a meaningful slice. |
service_offered | boolean | Some products only fit shops with a workshop. |
contract_renewal_date | date | For existing customers — filter for “renewing in the next 90 days”. |
loyalty_program_url | url | Useful for partnership campaigns; one click from the row. |
A saved filter combining country = Spain + cf.fleet_size gte 20 + cf.brands_carried contains Trek + status = prospect gives you the audience for next quarter’s premium-Trek-dealer campaign.
Worked example 2 — a podcast / radio-station directory
Section titled “Worked example 2 — a podcast / radio-station directory”A Company running outbound to media outlets might define:
| Field | Type | Why |
|---|---|---|
format | select, options = news, music, talk, mixed | The Product description and ICP are different per format. |
weekly_audience | number | Reach the model can compare directly against the ICP’s audience floor. |
genres | multiselect, options = the music genres you care about | ”Rock and electronic, but not classical” is a real targeting move. |
accepts_sponsorship | boolean | Hard prerequisite — set it after the first conversation. |
frequency_mhz | number | Geographic + technical filter for FM-only campaigns. |
The product description on a “podcast advertising platform for music shows” product references cf.genres and cf.weekly_audience directly, so the model has structured signals to score against — not just whatever it can extract from the station’s website.
Read next
Section titled “Read next”- Build a saved filter — every saved filter speaks the same
cf.<key>syntax. - Import accounts — map CSV columns to custom fields, or set batch-wide static values.
- Account — the row your custom fields hang off.
- ICP and scoring — how custom field values become part of the scoring prompt.