Skip to content

Messaging

LeadHunter is a communication log, not a sending engine. You talk to the account through the channel you already use — Instagram, LinkedIn, X, email, WhatsApp, phone, SMS — then paste the exchange into LeadHunter so the AI, the scoring, and the team-wide conversation history all have full context.

This is intentional. Sending is hard to do well across every channel an outbound team actually uses, and LeadHunter would compete badly with the dedicated tool for each. Keeping LeadHunter out of the sending path means it’s compatible with every channel you’ll ever care about, including the ones that don’t exist yet.

Each campaign-account (the join row between a campaign and an account — see Campaign → Campaign accounts) has its own conversation thread. Open any account in a campaign and you see the per-campaign timeline; open the account detail page instead and you see conversations aggregated across every campaign that account has been in, so the history follows the organisation even when the campaign changes.

The campaign-wide outreach inbox is a third view: open a campaign and switch to the Outreach tab to see every conversation across every account in that campaign, sorted by what needs your attention (recent inbound first, then drafts you haven’t sent yet).

Opening a conversation updates the page URL, so you can refresh without losing your place and share a link to a specific conversation with a teammate — they’ll land straight on that account’s thread.

The search box matches across the account name, contact name and email — and also the internal note and AI draft directions on each row. So if you stashed an external reference like an order number or CRM id in a contact’s note, searching that number jumps straight to them.

The four tabs (To contact, Awaiting reply, Responded, All) split the inbox by where each account sits in the funnel. The Filters panel below the search box narrows further — it opens by default with Approved + Excellent pre-selected so you land on the “work this now” queue every time you load the page; clear them to widen the view:

  • Review status — only accounts you’ve approved, only ones still pending review, only rejected ones. Useful for working through the approved queue end-to-end before touching anything questionable.
  • Match label — keep only excellent, moderate, or mismatch AI matches.
  • Score range — minimum and maximum on the AI score (0.0–10.0). Set min to 7 and ignore everything else until you’ve worked the best fits.
  • Channel — show only accounts you’ve already touched on Instagram, LinkedIn, email, WhatsApp, phone, SMS, or X. The channel filter looks at every message in the thread, not just the latest one — useful when you’ve drifted between channels with the same account.

Tab counts stay constant when filters are on, so the badges always tell you the true campaign size. Only the visible list (and the count at the bottom) reflects the filters. Pick any combination — they all AND together. Hit Clear all to start over.

Each row in the inbox carries a free-text Internal note that only you and your team see. It’s per-campaign-account, not per-account — the same lead in two campaigns (e.g. a sales campaign and a press campaign) has two independent notes, because the prep context usually differs.

Typical uses:

  • “demo live since Mon — wait a few days before pinging” (you built a custom demo and want it to bake before reaching out)
  • “warm intro coming from Ana next Thu — don’t outbound until then”
  • “called once, no answer; try after 4pm Madrid time”
  • “prefers LinkedIn over email”

When set, the note shows as a sticky-note snippet on the row preview (left sidebar) so you can scan the queue and spot which leads have context attached. Click the row and the editor lives in the conversation pane (just below the AI draft directions box); edits save automatically when you click away from the textarea. Saving the note doesn’t move the account through the outreach funnel — it’s purely a reminder field. The note is never sent to the AI — if you want the drafter to act on something, put it in the AI draft directions instead (see Goal-aware drafting).

If you need a note that follows the lead across every campaign (e.g. “this is Ana’s old boss”), use the account-level notes on the account detail page instead. The two fields are independent.

FieldWhat it holds
directionoutbound (you sent) or inbound (they sent).
channelWhich surface the message lived on. Eight choices: email, linkedin, instagram, twitter_x, whatsapp, phone_call, sms, other. The per-message channel is what the cohort funnel attributes against later, so picking the right one matters.
generation_modeHow the text was produced — one of five (see below).
statusdraft (scratch — doesn’t move the funnel) or sent (logged as out / in).
original_textAuthoritative version: what the lead actually sent or received, in their language.
original_languageISO 639-1 code of original_text.
translated_textOptional reader-friendly view of the same content in another language.
translated_to_languageISO code of translated_text.
sent_atWhen the message was sent. For modes other than Log sent, defaults to “now”. For Log sent, you set it explicitly to when it really went out.
created_byThe user who logged the message.
Auditcreated_at, updated_at.

There’s no separate “subject” or “thread id” — threading is implicit (every message attached to the same campaign-account belongs to the same thread).

ModeDirectionUse when
AI draftoutboundFirst outbound message in this thread. AI writes three drafts, each a different approach.
AI continueoutboundContinuing a thread that already has history. AI reads the prior messages and proposes three next moves.
Type & translateoutboundYou wrote the message yourself in your language. LeadHunter shows the translation into the account’s language and sends the translation.
Log sentoutboundYou already sent it elsewhere (your own email client, on LinkedIn directly, …). Paste the text to record it.
Inbound pasteinboundThe account replied. Paste their reply; LeadHunter shows you the translation into your reading language.

When you hit Draft with AI, you don’t get one take — you get three, each with a short label (Direct & concise, Warm & personal, Outcome-led, …) and a one-line “why”. The angles genuinely differ — a punchy one-liner, a relationship-first opener, a value/outcome pitch — so you can choose the register that fits the account instead of nudging a single draft toward what you wanted. Each one shows its approach, the message, and the reasoning.

Two buttons on every draft:

  • Copy — puts that exact text on your clipboard so you can paste it into Instagram, LinkedIn, your email client, or wherever the conversation actually happens. It flips to Copied for a moment so you know it took. Since LeadHunter logs rather than sends, copy-then-paste is the normal last step.
  • Use this — loads that draft into the editable box below so you can tweak it and Log message to record it here. The first approach is loaded automatically, so you can edit-and-log immediately or switch to another with one click.

Hit Redraft for three fresh approaches if none land. The Copy button also lives under the editable box, so whichever draft you settle on is one click from your clipboard.

Knowing which mode wrote which field helps when re-reading old threads:

Modeoriginal_texttranslated_text
AI draft / AI continue / Log sentThe sent message (account’s language)(empty)
Type & translateThe translated/sent message (account’s language)Your original draft (your language)
Inbound pasteThe account’s reply (their language)Translation into your reading language

The asymmetry on Type & translate is the one users miss most: original_text is the message that actually went out, not your scratchpad draft. If you go back six months and read a translated outbound, the version that left your hands is the canonical one.

Every AI draft, AI continue, Type & translate, and Inbound paste message triggers an LLM call (translation in/out, prior-thread summarisation when there’s history). The cost is small per message — a fraction of a cent on Gemini Flash by default — but attributes per (Campaign, Account) under API costs. Log sent doesn’t call the LLM (you provide the final text directly), so it’s free.

If translation fails (rare; usually a transient provider error), the message is still saved with original_text only — translated_text stays empty and you can retry from the message panel.

The drafter writes a different kind of message depending on the campaign’s goal. Same product, same account — but a sales campaign opens with a cold pitch, a press campaign opens with a newsworthy angle, a recruiting campaign asks the candidate for 15 minutes about a specific role, a partnership pitch proposes a collab shape, an investor approach leads with a one-line traction signal. The 0–10 scoring scale and the typed reasons stay consistent across goals; what shifts is the message’s voice and call-to-action.

Several things compose into the draft prompt, in order:

  1. Message intent — derived from Campaign.goal. Reads as “write a cold sales opener — introduce the product, name one specific thing about the lead, end with a soft ask for a short call” for sales, “a short press pitch — newsworthy angle first, why-now hook, no sales language” for press, and so on for the eleven goals.
  2. Target persona + product context — the (Product, Goal) ICP is rendered into the prompt so the model has the persona definition the campaign rates against.
  3. Account context + thread history — the account itself, plus the role_in_campaign override if set (see below).
  4. Your directions — the campaign brief and per-account directions you set yourself (see below). These come last so the model weighs them most heavily.

If the campaign’s goal changes mid-flight, the next draft you generate uses the new intent immediately. Existing already-drafted messages don’t auto-rewrite.

Steering the draft: campaign brief + per-account directions

Section titled “Steering the draft: campaign brief + per-account directions”

The goal sets the kind of message; you set the specifics. Two free-text fields let you hand the drafter directions in your own words — tone, length, angle, what to mention, what to avoid. Both are optional, blank by default, and take effect on the very next draft (nothing to regenerate).

  • AI outreach brief — campaign-wide. Open the AI outreach brief panel at the top of the campaign’s outreach inbox (left sidebar) and type directions that apply to every draft in the campaign: “keep it under 60 words, friendly, no emojis; lead with the integration angle”, “always reference that we’re a local Barcelona company”, “formal register, no first names”. A small dot on the panel marks a campaign that has a brief set. It saves when you click away.

  • AI draft directions (per account) — open any conversation and you’ll see an AI draft directions box at the top, right above the internal note. Type directions that apply to this one account: “they demoed last week — reference it”, “warm intro from Ana is pending, mention it lightly”, “do not bring up pricing yet”. These are layered after the campaign brief and win on any conflict, because they’re the most specific. Accounts with directions set show an ✨ directions marker on their inbox row.

The two stack: the campaign brief sets the house style, the per-account directions handle the one-off context. Neither is the same as the internal note — the note is private prep that the AI never reads; these two fields are written for the AI.

If the same account is in two campaigns, its directions are per-campaign (they live on the campaign-account, like the internal note), so a sales angle and a press angle stay independent.

Sometimes the same Account plays different roles in different campaigns. A SaaS company is your customer (sales), but their CTO is also a regular podcast guest (press), and the head of engineering recently spoke at your conference (event). Each campaign needs the message to read with the right voice.

Setting role_in_campaign on a CampaignAccount tells the drafter: “for this campaign, treat this account as <role> regardless of whatever primary tags it carries elsewhere.” The drafter respects the lens — a press campaign with role_in_campaign='press' produces a press pitch even when the account’s primary relationship_types say client. Blank means “use the account’s tags as-is.”

Setting it is a click in the campaign’s accounts panel: each row’s chip cluster shows a faint dashed + role affordance when no override is set, or a colored chip with the override label when one is set. Clicking opens a popover with all 13 relationship-type options — pick one to apply, or pick “Use the account’s tags (no override)” to clear.

This is the right escape hatch when one account legitimately wears multiple hats and you don’t want to blur them across campaigns.

LeadHunter logs messages, it doesn’t send them — so between “sent” and “responded” the funnel used to be blind. Tracked links close that gap: a short link tagged with the campaign and contact that records every click.

In the conversation pane, open the Tracked links panel:

  1. Paste the destination (the field pre-fills with the account’s website) and an optional label, then click Create.
  2. Copy the short link and paste it into your message wherever you’d have pasted the long URL.
  3. When the contact clicks it, they’re redirected instantly to the destination — and the click lands on the conversation: the panel shows the click count and the last-click time, and the inbox row gets a clicks chip so you can spot warm contacts at a glance.

Good to know:

  • Bot clicks don’t count. Link previews (WhatsApp, Slack, LinkedIn unfurlers) and crawlers hit your links constantly; LeadHunter logs them but keeps them out of the click count, so “2 clicks” means a human clicked twice.
  • No personal data is stored about the clicker — no IP addresses, just the time, the browser family, and where the click came from.
  • Destinations can’t be edited after creation. A link you already sent must keep meaning what it meant when you sent it; create a new link instead.
  • Deleting a link makes it stop redirecting — and deletes its click history.

Clicks also feed the dashboard funnel: a Clicked stage now sits between Initiated and Responded (with its own cohort click-through-rate chart and a column on the per-product / per-campaign stats tables), so you can see how much of your outreach gets attention even before anyone replies.

Outbound messages render in the account’s language if you’ve set one. Otherwise LeadHunter walks a fallback chain until it finds a setting:

Account.language
→ Campaign.communication_language (per-campaign override)
→ Product.communication_language (product default)
→ User.communication_language (your personal default)
→ English

The campaign-level override means you can run one campaign of the same product in Spanish (for the Spain market) and another in English (for the UK) without duplicating the product. See Campaign → Language.

Your reading language — what LeadHunter translates inbound text into — is your User-level communication language. It’s independent of the UI language, so you can run the dashboard in English while writing to and reading from accounts in Spanish.

Override the languages for one conversation

Section titled “Override the languages for one conversation”

The resolved direction shows as an editable From → To at the top of the composer. The two fields are pre-filled from the chain above, but you can change either one — type any language or pick a common one from the dropdown, and use the swap button to flip the direction. The override drives both the AI drafts and the type-&-translate output for this conversation only; it isn’t saved. Switch to another conversation (or refresh) and you’re back to the resolved languages, and a reset link restores them at any time. Use it for a one-off — say this lead’s account language is detected as Spanish but you happen to know they prefer English. To make the change stick for every future message, edit the account’s language (or the campaign / product / your own default) instead.

Logging a message as sent advances the pipeline automatically:

  • Outbound sent → campaign-account outreach_status becomes sent (or bounced/responded), and the dashboard funnel counts the account as initiated.
  • First outbound on a prospect account → account-level status advances to contacted with source: campaign_outreach in the audit trail.
  • Inbound paste → campaign-account outreach_status becomes responded; the dashboard funnel counts the account as a response, cohort-attributed back to the original initiation date.

All forward-only. Already-customer accounts don’t regress when you log a follow-up; a second outbound doesn’t re-fire the prospect → contacted promotion.

Drafts (status='draft') don’t fire any of this. They’re scratch space — useful while you iterate, but they don’t move the funnel until you flip them to sent.

The auto-promotion model assumes you initiate, then they reply — the common outbound case. For inbound-acquired accounts (Adwords form-fill, Instagram DM, referral), the account is created with an acquisition_channel and starts at status: contacted automatically before any message is logged. See Track inbound leads.

When the first message on such an account is your reply rather than their initial reach-out, log their initial message as an Inbound paste first (with the right sent_at), then your reply as AI continue or Type & translate. The funnel attribution works correctly either way — initiation is your first outbound, response is their first inbound; their original outreach pre-dating LeadHunter doesn’t count as a response.

Two layers of opt-out apply to outbound message logging:

  • Blanket DNC — outbound to a status='do_not_contact' account is blocked on every campaign goal. The UI shows a clear error and points at the account’s status history so the operator can see when and why the flag was set. No override.
  • Per-purpose DNC — outbound is blocked when the campaign’s goal is in the account’s do_not_contact_purposes list. An account that opted out of sales can still receive research interview asks or press pitches; an account that opted out of press can still receive sales messages. The list is the GDPR / CASL-shaped consent surface, distinct from the blanket flag. Recorded via POST /api/accounts/{id}/record-dnc/ (or whichever UI control surfaces it). See Account → Per-purpose opt-outs.

Other relationship-type guardrails (competitor, personal_network, soft blocks like supplier/investor/press/analyst/candidate) only apply to campaign bulk-add — not to logging individual messages on accounts already in a campaign. The rationale: if it’s in the campaign, it cleared the bulk-add filters.

  • Log a conversation — the operator-side walkthrough with a worked outbound → reply → continue example.
  • Campaign — the language-resolution chain in full, plus the outreach guardrails.
  • Account — the row whose lifecycle status the message log moves.