Skip to content

Campaign

A Campaign is one outbound initiative against a list of accounts. Every campaign points to exactly one Product, which means it inherits that product’s fit criteria, default language and scoring calibration. The campaign is the unit you operate against — bulk-add accounts to it, score them, review the scores, log conversations against them, and measure spend against the outcomes it produced.

Every campaign also carries a purpose (the goal field in the API) — why you’re reaching out. The default is sales prospecting; ten others cover partnership recruitment, press / visibility, investor outreach, recruiting, customer expansion, win-back, and more. The purpose picks which of the product’s fit criteria the campaign scores against, and reshapes the message intent and the guardrails — same product, different intent, different outcome. See Outreach reasons and targets.

Switching a campaign’s purpose mid-flight (Change purpose on the Settings & costs tab) is allowed but the existing scores stay as they were (they were rated against the previous purpose’s rubric). When that happens, the campaign’s Overview shows a banner saying how many accounts have fit scores from a different purpose, with a link to Review, where you rescore them (the cost estimate comes first).

BucketFields
Identityname, slug (auto-generated from name, collisions suffixed within the project), project, product
Intentgoal (sales / partnership / press / investor / recruiting / customer expansion / win-back / event / research / influencer / renewal — see Outreach reasons and targets)
Scopeaudience_config (saved-filter / manual selection)
Languagecommunication_language (inherits from Product when blank)
Workflowstatus (operator-facing stage), scoring_status (background-job state), archived_at
Cached statisticstotal_accounts, scored_accounts, approved_accounts (shortlisted), outreached_accounts, responded_accounts, response_rate
Scoringscoring_brief (optional; a campaign with a brief keeps its own scores), fit_criteria (which criteria and version it scores against, read-only), score_distribution
Provenancecreated_by, created_at, updated_at

The cached statistics are maintained as you operate — outbound logs bump outreached_accounts, logged inbound replies bump responded_accounts, shortlisting bumps approved_accounts. The numbers on the campaigns list come from these columns, so they’re fast even on large projects. The campaign’s Overview shows live counts instead (see below).

Every page of a campaign shares one frame: the breadcrumb Campaigns / campaign name and five tabs.

TabWhat’s there
OverviewThe campaign’s name, purpose and status; six counts that each open the page where that work happens (Accounts, Not scored yet, To review, Shortlisted, Awaiting reply, Replied); Next for this campaign, the list of what is waiting on you; the fit criteria (Details and regenerate, Edit fit criteria); and the Campaign accounts panel, where you add and remove accounts.
ReviewThe Review and shortlist page: fit score bands, decisions one by one or in bulk. See Review thousands of accounts.
OutreachTwo views: Inbox, the campaign’s conversations (Messaging), and Follow-up, who is due another attempt (Review your outreach).
BoardThe campaign’s workflow board, or Create board.
Settings & costsName, purpose, status, language, scoring brief, the global-stats exclusion, enrichment, Costs & expenses, and duplicate, archive or delete.

Wherever a campaign account shows in a list (a Review row, a Follow-up row, a board card), opening it gives the same panel: the conversation with its composer, the next step, the review decision and the board stage. In the Outreach inbox the same content fills the right-hand pane.

Each campaign carries its own communication language. It drives the language of every piece of AI-generated content the campaign produces — the reasons attached to each fit score and the outbound message drafts. Leave it blank to inherit the language set on the Product; set it explicitly to override the product default for this campaign only. This lets you run one campaign of the same product in Spanish for the Spain market and another in English for the UK without duplicating the product.

When sending a message, language resolution checks the account first, then walks the chain Account → Campaign → Product → User → English. So an account whose own language is set to Catalan always reads Catalan, regardless of which campaign reaches them.

A campaign has a status, shown as a badge on its Overview and changed under Settings & costs → Status (it asks you to confirm):

  1. Draft — Created, no accounts yet.
  2. Fit criteria — Check the product’s fit criteria for the campaign’s purpose, adjust them, approve.
  3. Audience — Decide where accounts come from (the Accounts list, a saved view, an import).
  4. Review — Each account in scope gets a fit score and a decision.
  5. Outreach — You’re actively reaching out.
  6. Active — Ongoing campaign with mixed reach-out and reply work.
  7. Paused — Temporarily on hold; everything is preserved. Pause shows while the campaign is Active, Resume while it is Paused.
  8. Completed — Campaign is wrapped up. The Complete button (next to the status, while the campaign is Active or Paused) asks you to confirm first, because a completed campaign can’t be resumed or moved to another step; its accounts, scores and conversations are kept.

The status is an operator hint, not a hard state machine — LeadHunter doesn’t refuse to score a campaign because its status says “Outreach”. Changing it moves no accounts and starts nothing. You can move freely between stages, and the name is shown to your team for context, not enforced.

Archive is orthogonal: any campaign can be soft-hidden with Archive, under Settings & costs → Duplicate, archive or delete (sets archived_at; Unarchive brings it back). Archived campaigns disappear from the default list but stay in the database with every conversation, score, expense, and audit-trail entry intact. Use archive for “campaign is done but I want the history”; use Delete (same place) only when you’re sure. Delete opens a dialog where you type the campaign’s exact name; the Delete button stays disabled until the text matches, and Esc cancels. Deleting several selected campaigns at once asks you to type DELETE 3 CAMPAIGNS (with your count) instead.

Rename a campaign under Settings & costs → General: edit Campaign name and save.

A campaign can be flagged Exclude this campaign from global stats under Settings & costs → Scoring and statistics. When the box is ticked, the campaign’s outreach, replies, and inbound-close events stop contributing to:

  • the funnel charts on Insights → Funnel (initiated / replied / closed totals and daily series),
  • the cohort response rate and cohort close rate,
  • the Strong fits reached tile and its 30-day rate (any account whose only campaign membership is the excluded campaign drops out),
  • the by product and by channel roll-ups on Insights → Breakdown,
  • and the per-product CAC ratios.

The campaign’s own pages are unaffected — its counts, score bands, accounts panel, costs panel, and cost summary all still show real numbers. On Insights → Breakdown, the campaign row is still listed with its own totals, but tagged with an amber Excluded chip so you can tell at a glance that it isn’t being counted toward the project-wide funnel.

The Strong fits reached rule is account-level rather than event-level: an account stays counted as long as it has at least one membership in a non-excluded campaign (or no campaign membership at all, e.g. an account that came in through an inbound channel). So enrolling the same account in a second, real campaign restores it to the count automatically.

Use the flag for campaigns whose numbers would mislead the funnel rather than describe it:

  • Experiments and calibration runs — a one-off test campaign you used to dial in the fit criteria, not real outreach.
  • Bulk imports of pre-existing client relationships — accounts that became clients before LeadHunter ever sent a message, with a single retroactive logged message / “status → customer” event each. Counting those as a 100% close-rate cohort would tilt the funnel for weeks.
  • Internal sanity checks — a campaign you reach out to yourself with from another seat, just to verify formatting.

One subtlety: status → customer transitions live on the Account, not on the campaign, so flipping this checkbox doesn’t retroactively unwin a client. If a particular conversion shouldn’t count, flip the account back to prospect first, or stamp the status_history entry as source=pre_existing — the funnel already excludes that source.

Separate from the workflow status above, every campaign carries a scoring_status that reflects the background scoring job:

  • idle — nothing in progress.
  • running — a scoring run is processing this campaign’s accounts. The Review tab and Runs → Campaign scoring show its progress and cost, with Cancel run.
  • completed — the most recent scoring batch finished successfully.
  • failed — the most recent run hit an error or was cancelled. Scores already made are kept; Score N remaining finishes the rest.

Scores are shared by every campaign of the same product and purpose that has no scoring brief; a campaign with a brief keeps its own. See How scoring works.

Once scoring has run, the campaign’s Review tab shows fit score bands: 8–10 (Strong fit), 6–8 (Good fit), 4–6 (Weak fit), 0–4 (Poor fit) and Not scored yet, each with how many of its accounts are to review, shortlisted, deferred or not a fit. Click a band to work only its accounts. It’s the fastest way to read whether the fit criteria are well-calibrated — a healthy distribution skews bimodal (strong-fit and poor-fit piles, a modest middle); a flat or all-middle distribution usually means the fit criteria or example websites aren’t sharp enough yet. See How scoring works.

The join row between a campaign and an account is a CampaignAccount. It carries per-campaign workflow data — the fields you’ll touch most as an operator:

FieldWhat it holdsWhere it moves
review_statusYour decision on this account for this campaign. Shown as To review (pending), Shortlisted (approved), Deferred (deferred) and Not a fit (rejected).Set on the campaign’s Review tab (or in the campaign-account panel), one account at a time or in bulk. Each decision records who made it, when, its reason and whether it was a bulk change; bulk changes can be undone.
outreach_statusWhat’s happening with outbound?not_started → draft → scheduled → sent → bounced / responded (shown as Replied). Forward-only.
override_scoreOptional manual score that supersedes the fit score for this campaign only. The shared fit score doesn’t move.Set from Set score by hand in each row’s menu on the campaign’s Review tab. The dialog accepts a value 0–10 and a Clear button reverts to the AI’s score.
contact_name / contact_title / contact_email / contact_linkedinWhich specific person the campaign’s outreach is aimed at, when the underlying account has multiple contacts.Shown in the header of the campaign-account panel.
tagsFree-form per-campaign labels — “Q2 priority”, “warm intro”, etc.Edited on the row.
notesPer-campaign notes that don’t belong on the account itself.The Internal note in the campaign-account panel.

The underlying fit score belongs to the account, the product and the purpose (or to the campaign, when it has its own scoring brief), not to the CampaignAccount, so adding an account to a second campaign of the same product and purpose doesn’t trigger a re-score.

The day-to-day loop runs through these CampaignAccount fields:

  1. Scoring fills the fit score (ai_score in the API) for every row when the campaign first scores.
  2. You review — read the score bands and the reasons, then Shortlist, Defer or mark Not a fit, per account or in bulk (a band, the top N, every match). Or set override_score if the AI was clearly off. See Review thousands of accounts.
  3. For shortlisted rows you kick off outreach — open the account in the Outreach tab (or click its row in Review), draft and send. Each outbound message you log flips outreach_status to sent and counts the account as initiated in the funnel on Insights → Funnel.
  4. When the account replies, paste the inbound — outreach_status flips to responded (Replied) and the funnel’s reply cohort gets credit.

The campaign’s tabs cover the whole loop — accounts on the Overview, the queue in Review, conversations in Outreach, spend in Settings & costs — and the campaign-account panel keeps one account’s conversation, next step and decision in one place.

Every Shortlist or Not a fit you mark on one account is a calibration signal. Once a product and purpose cross ~10 of them across their campaigns, the most recent ones start flowing into the scoring prompt as concrete examples. Bulk decisions are recorded as bulk and never feed the calibration. See How scoring works → The feedback loop.

This makes the first campaign on a new product the most important one to be thorough on the review step — every decision shapes the next several hundred scores.

Once scoring finishes, the bottom of the list is usually noise — accounts the model scored below 5 (the API label mismatch) that aren’t worth your outreach time. The Campaign accounts panel on the Overview has a Remove fits under 5 button next to the In this campaign count that clears them in one pass.

The flow is two-step: clicking the button runs a dry-run preview and shows the count it would remove, then a dialog where you type REMOVE 12 ACCOUNTS (with your count). Nothing is deleted until you confirm.

What it targets: only accounts scored below 5. That is every Poor fit (below 4) plus the lower half of Weak fit (4 to 5). Accounts scored 5 or more are never touched. Unscored accounts are also left alone — they haven’t been judged yet, so the model has no opinion to act on.

What it keeps by default, even when they’re scored below 5:

  • Accounts you’ve shortlisted. Your judgment beats the model’s — if you’ve already vetted the account, the AI’s low score is the wrong one to honour. The preview dialog tells you how many were kept on this axis.
  • Accounts where outreach has already started — any outreach_status other than not_started (draft, scheduled, sent, bounced, responded). Removing them would orphan the conversation thread. The preview surfaces this count separately so you know exactly what’s being skipped and why.

Both defaults can be turned off if you really want a hard prune, but the safe defaults are the right call almost always.

The removal is scoped to this campaign only. The same account in a second campaign of the same product is untouched — and the underlying score isn’t deleted either, so if you later add the account to a new campaign with tightened fit criteria and rescore, the row gets a fresh chance.

This is a destructive operation (no soft-delete, no archive — the CampaignAccount rows are gone). The account itself, its contacts, and any conversation messages on other campaigns are unaffected. If you removed a row by accident, re-add it from the Available to add column.

The campaign-accounts panel has a Download CSV button next to Add new account. By default it pulls the rows you’ve shortlisted (Shortlisted only) with four columns — Name, Website, Location (City and country combined), and AI Score. One click, file lands in Downloads.

The same modal lets you widen the row filter (Shortlisted + to review, or Every status, which adds deferred and not a fit) and tick on any of ~40 optional columns grouped by topic:

  • Contact info — Phone, email, the per-campaign selected contact (name, title, email, LinkedIn)
  • Address parts — Street, city, state/region, postal code, country, latitude/longitude as separate columns (handy when importing into another CRM that wants them split)
  • Account socials — LinkedIn, Instagram, X, Facebook, YouTube, TikTok
  • Business profile — Business type, specialization, year founded, employee count bucket, language, rating
  • Pipeline and tags — Account status, relationship types, acquisition channel, captured value, campaign tags
  • Workflow and audit — Review status, outreach status, override score, score reasons, internal notes, source, imported ID, date added
  • Custom fields — Every per-project custom field shows up automatically in its own section, so columns like “Annual revenue” or “Industry segment” are one click away

A typo in the column name returns an error instead of silently emitting a blank column, so you can’t end up with a file that looks correct but is missing something.

Bulk-adding accounts to a campaign honours every protection LeadHunter knows about, in one pass. The full matrix depends on the campaign’s purpose — a Press campaign doesn’t block press accounts, a Recruiting campaign doesn’t block candidate accounts, a Customer expansion campaign treats clients as the target audience. See Outreach reasons and targets for the per-purpose table.

Two invariants survive every purpose:

  • do_not_contact accounts are always blocked. GDPR / opt-out is sticky regardless of campaign intent. To re-engage someone, change their status manually first.
  • personal_network accounts are always hard-blocked. Never overridable, for any purpose. competitor accounts are hard-blocked too, except in press, event and research campaigns, where competitors are legitimate targets.

Everything else flexes per purpose: clients are admissible in expansion / renewal / event / research / partnership campaigns and blocked elsewhere (overridable with Include existing clients (for upsell), include_customers in the API); the supplier / investor / press / analyst / candidate soft-blocks each turn off for the purpose that targets that type.

When the UI shows you blocked accounts after a bulk-add, it groups them by reason and tells you exactly which flag flips them in — phrased in terms of this campaign’s purpose. A bulk-add into a Sales campaign that hit press accounts will tell you “this Sales campaign soft-blocks press accounts; pass include_press=true to override, or switch the goal so press is the target audience”. A Press campaign would not produce that message because press accounts aren’t blocked there.

Guardrails apply at the moment of bulk-add. Once an account is in a campaign, logging individual outbound messages to it doesn’t re-run these checks (DNC is the exception — DNC is enforced at message-send time too).

Every campaign carries a ledger of expenses — Adwords spend, agency fees, internal labor hours, software, content, events. Add entries from the campaign’s Settings & costs tab or with the Log expense button on the campaigns list. The campaign then computes cost-of-acquisition:

  • Cost per outreached account — total spend ÷ accounts you’ve reached out to.
  • Cost per replied account — total spend ÷ accounts that have replied.
  • Cost per close (a won client for a sales campaign, or the purpose’s own close, such as a press reply) — on the campaign’s Costs panel and on Insights → Breakdown, cohort-attributed over the last 30 days.

Mixed currencies show per-currency totals without auto-conversion — LeadHunter doesn’t carry an FX layer. CAC is only computed when expenses share one currency; otherwise the UI surfaces a per-currency breakdown and explains the missing ratio.

Auto-tracked API costs (LLM tokens, Google Maps calls, Tavily searches) sit in their own card on the same panel: USD total + a per-model breakdown of which model spent what. It’s kept out of the CAC math because it’s in USD and your user-entered expenses may not be, but the two cards together answer the “what did this campaign actually burn?” question. Per-account enrichment cost is exposed on the account detail.

See Track campaign costs and CAC.

The Follow-up view of the campaign’s Outreach tab (Review your outreach) shows how many attempts each account has had, who is due another and who replied, with no next steps or notifications created. A board is the fuller option, with stages, deadlines and next steps.