Skip to content

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.

TypeWhat it storesExample
textShort single-line stringLicense number
textareaLong multi-line stringNotes about the location
numberInteger or decimalNumber of franchises
dateYYYY-MM-DDContract renewal date
booleantrue / falseHas loyalty program?
selectOne value from a listTier: Gold / Silver / Bronze
multiselectMany values from a listLanguages spoken
urlValidated URLReviews page

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 flagboolean
A count you’ll want to filter or sort bynumber
A specific datedate
A clickable URLurl
A short opaque string (license number, account ref)text
A long free-form blobtextarea (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.

From Settings → Custom fields → Add field, each definition carries:

PropertyWhat it does
KeyMachine name. Lowercase, snake_case, starts with a letter (regex ^[a-z][a-z0-9_]{0,79}$). Shows up in filter expressions as cf.<key>.
LabelHuman-readable name shown on account forms (e.g. “Fleet size”).
TypeOne of the eight above.
Product scopeOptional. Leave it on Company-wide and the field shows on every account; pick a product and the field is grouped under that product on the account screens. Handy when a field only makes sense for one product line.
OptionsRequired for select / multiselect. The exact strings counted as valid values.
Deep-link templateurl fields only — turns the field into a one-click link into an external system. See Deep links into external systems.
Help textOne-line hint shown next to the input on the account form.
RequiredWhen 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.
OrderDisplay 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. Within a company you can narrow a field further to a single product with Product scope — the value still lives on the account, the product is just how it’s grouped in the UI.

Two paths an operator can use:

  1. 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.
  2. 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, language). If you need a custom field populated automatically, fill it on import or on the account form.

A url field can be more than a place to paste a link. Give it a deep-link template and LeadHunter builds a one-click link into your CRM, billing system, admin panel — anywhere an account also lives — without anyone pasting a URL per row.

You write the template once, with {placeholders} LeadHunter fills in per account:

https://crm.example.com/contact/{imported_id}
https://your-admin.example.com/client/{value}

Three kinds of placeholder:

PlaceholderFilled with
{value}A value the operator types into this field on the account (use this once per template).
{cf.<key>}The value of another custom field on the same account.
Account fieldsThe account’s own data — {imported_id}, {id}, {website}, {email}, {phone}, {name}, {city}, {country}, {google_place_id}.

The standout is {imported_id}. When you import a list, LeadHunter remembers each row’s original ID from the source system. A template like https://crm.example.com/contact/{imported_id} then deep-links every imported account straight back into that system — no per-account typing at all. That’s the quickest way to make LeadHunter a launchpad into the tools your team already uses.

Set it up in Settings → Custom fields → Add field:

  1. Choose type URL.
  2. Fill Deep-link template with your URL and placeholders. A live preview shows what the link will look like.
  3. Optionally set a Link label (e.g. “Open in CRM”, “Enacast admin”) — that’s the text shown instead of a raw URL. It falls back to the field’s label.
  4. Optionally set Product scope if the link only applies to one product.

On each account, the field renders as a labelled, clickable link that opens in a new tab. If the template needs a {value}, you’ll see an input to type it; if it’s built entirely from account data, there’s nothing to fill — the link just appears. When a referenced piece of data is missing on an account (e.g. a row with no imported ID), no broken link is shown for that account.

A few practical notes:

  • Safe by design — templates must point at an http(s) web address with a fixed host (e.g. https://crm.example.com/…); placeholders go in the path or query string, not the host. Values you type are encoded into the link so odd characters can’t break it.
  • Plain URL fields still work — leave the template blank and the field behaves like before: paste a whole URL and it renders as a link.
  • Exports include the built link — when you download a campaign’s accounts as CSV, a deep-link column carries the finished URL, ready to hand to another tool.
  • One manual value per field — if you need two different external IDs on the same account, make two fields.

The other direction shipped: webhooks. Deep links take you into the other system; outbound webhooks send data the other way — notifying your external system the moment an account is created, changes status, or replies. Pulling state back (full two-way sync) is the remaining roadmap step.

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.

Reference custom fields in saved filters with the cf. prefix:

match: all
conditions:
country = Spain
cf.tier in [Gold, Platinum]
cf.contract_renewal_date lt 2026-06-30

Custom 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.

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 in merge_history so you can read what was overridden.
  • multiselectunion of both arrays. No loss.

Nothing is silently dropped, even on a conflict.

ActionEffect
Add a fieldInstant. Existing accounts immediately gain the field with a null value; new accounts pick it up on the next form load.
Rename label or help textInstant. Filters and forms keep working — they reference the key, not the label.
Rename a keyNot 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 fieldRemoves 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.
  • 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 a textarea custom 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.

For a Company running outbound to bike shops, useful custom fields might be:

FieldTypeWhy
fleet_sizenumberTarget by store size. “Shops with ≥20 bikes in stock” is a meaningful audience.
brands_carriedmultiselect, options = the brands you care about”Shops that carry Trek but not Specialized” is a meaningful slice.
service_offeredbooleanSome products only fit shops with a workshop.
contract_renewal_datedateFor existing customers — filter for “renewing in the next 90 days”.
loyalty_program_urlurlUseful 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:

FieldTypeWhy
formatselect, options = news, music, talk, mixedThe Product description and ICP are different per format.
weekly_audiencenumberReach the model can compare directly against the ICP’s audience floor.
genresmultiselect, options = the music genres you care about”Rock and electronic, but not classical” is a real targeting move.
accepts_sponsorshipbooleanHard prerequisite — set it after the first conversation.
frequency_mhznumberGeographic + 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.

  • 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.