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.
The conversation thread
Section titled “The conversation thread”Every message belongs to an account; a campaign is optional context. A message logged under a campaign also belongs to that campaign-account thread (the join row between a campaign and an account — see Campaign → Campaign accounts) and counts in the campaign’s funnel. A message logged with no campaign — a trade-fair follow-up, a chat with an existing client, a reply from an account that came to you on its own — lives on the account only.
The account’s Conversations page (open the account, then Open all conversations under Conversation on its Activity tab) is the full history: every message with the account in one chronological stream, across every campaign plus the ones logged without a campaign, each with its campaign as a badge. You can filter it by channel and by campaign (No campaign shows the account-only messages), load older messages with Load older, and log new ones from the composer at the bottom — under No campaign by default, or under one of the account’s campaigns. See Log a conversation. Open an account inside a campaign instead and you see that campaign’s thread only.
The campaign-wide outreach inbox is a third view: open a campaign and switch to its Outreach tab (the Inbox view) 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). The account list is on the left; the selected account’s thread, composer, next step and review decision are on the right. The same content opens as a panel when you click a row in the campaign’s Review or Follow-up, or Open conversation in a board card’s drawer.
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.
Inbox filters
Section titled “Inbox filters”The search box matches across the account name, contact name and email — and also the internal note and AI draft directions on each row. Type several words and it keeps the rows that match every one of them. 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, Replied, All) split the inbox by where each account sits in the funnel. The Filters panel below the search box narrows further. It starts collapsed and its header says how many filters are on (2 active): the inbox opens with Shortlisted + Strong fit pre-selected so you land on the “work this now” queue; clear them to widen the view:
- Review decision — To review, Shortlisted, Deferred or Not a fit (the decisions from the campaign’s Review tab). Useful for working through the shortlist end-to-end before touching anything questionable.
- Fit — keep only Strong fit (8+), Good fit (6–8), Weak fit (4–6) or Poor fit (under 4) scores, or accounts Not scored yet.
- Fit score range — minimum and maximum on the fit score (0.0–10.0), your per-campaign score override included, the same as the review list. Set
minto7and 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.
Internal notes per campaign-account
Section titled “Internal notes per campaign-account”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 account 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 (the list on the left) so you can scan the queue and spot which accounts have context attached. Click the row and the editor is in the conversation pane under Internal note, folded below the composer and the next step (next to AI draft directions for this account); 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 note to yourself and your team. 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 Purpose-aware drafting).
If you need a note that follows the account across every campaign (e.g. “this is Ana’s old boss”), use the account’s own notes (Brief & notes on the account page) instead. The two fields are independent.
What a message carries
Section titled “What a message carries”| Field | What it holds |
|---|---|
direction | outbound (you sent) or inbound (they sent). |
channel | Which 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_mode | How the text was produced — one of five, one per composer mode (see below). |
status | draft (scratch — doesn’t move the funnel) or sent (logged as out / in). |
original_text | Authoritative version: what the account actually sent or received, in their language. |
original_language | ISO 639-1 code of original_text. |
translated_text | Optional reader-friendly view of the same content in another language. |
translated_to_language | ISO code of translated_text. |
sent_at | When the message was sent. The composer always records “now”; when you log through the API you can set it to when the message really went out (useful for Log already sent and backfills). |
created_by | The user who logged the message. |
external_id | Optional. The id the sending service gave the message (a WhatsApp message id, an email Message-ID). Makes logging safe to retry — see below. |
purpose | Which kind of outreach the message serves — one of the campaign purposes (sales, press, research, …). A campaign message takes its campaign’s purpose (the campaign’s goal field). A message logged without a campaign carries the purpose you picked, or none; you must pick one when the account has per-purpose opt-outs. Messages logged before 5 October 2026 have none. Can be changed while the message is a draft, not after it’s sent. |
| Audit | created_at, updated_at. |
There’s no separate “subject” or “thread id” — threading is implicit: every message of an account is in the account’s history, and every message logged under a campaign is also in that campaign’s thread for the account.
Logging from a script or an agent without duplicates
Section titled “Logging from a script or an agent without duplicates”If you log sends automatically (a WhatsApp bridge, a mail tool, an AI agent), a timeout can make it post the same message twice. Include the sending service’s own message id as external_id: the first post creates the message (201), and any repeat with the same id on the same account returns the message already logged (200) instead of adding a copy. Messages logged without an external_id behave as before.
The five drafting modes
Section titled “The five drafting modes”The composer has one tab per mode. The tab names are below, with the generation_mode value each one records.
| Mode | generation_mode | Direction | Use when |
|---|---|---|---|
| AI: first message | ai_draft | outbound | First outbound message in this thread. AI writes three drafts, each a different approach. |
| AI: continue thread | ai_continue | outbound | Continuing a thread that already has an outbound message (the tab is disabled until then). AI reads the prior messages and proposes three next moves. |
| Type & translate | type_translate | outbound | You wrote the message yourself in your language. LeadHunter shows the translation into the account’s language and logs the translation. |
| Log already sent | log_sent | outbound | You already sent it elsewhere (your own email client, on LinkedIn directly, …). Paste the text to record it. |
| Log inbound reply | inbound_paste | inbound | The account replied. Paste their reply; LeadHunter shows you the translation into your reading language. |
Each tab keeps its own text (since 2026-10-03): switch from a half-written Type & translate message to Log inbound reply to log their answer first, and your message is still there when you switch back. A tab you haven’t used starts empty. The tabs are locked while a draft or translation is being generated. The composer opens on the mode and channel you used last; in a thread that already has messages, the channel of its latest message wins.
Three approaches, pick one
Section titled “Three approaches, pick one”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.
Whatever you type in the editable box is kept per conversation while you work: switch to another account in the inbox and back, and your unsent text is still there. It lives in that browser tab only (closing the tab or logging out clears it), so log the message when it’s ready.
How modes populate the two text fields
Section titled “How modes populate the two text fields”Knowing which mode wrote which field helps when re-reading old threads:
| Mode | original_text | translated_text |
|---|---|---|
| AI: first message / AI: continue thread / Log already sent | The sent message (account’s language) | (empty) |
| Type & translate | The translated/sent message (account’s language) | Your original draft (your language) |
| Log inbound reply | The 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.
AI drafting and translation cost
Section titled “AI drafting and translation cost”Every AI: first message, AI: continue thread, Type & translate, and Log inbound reply 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 the default model — and shows under API costs against the campaign and the account (against the account only when the message has no campaign). The AI drafting modes need a campaign; without one the composer offers Log already sent, Log inbound reply and Type & translate. Log already 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.
Purpose-aware drafting
Section titled “Purpose-aware drafting”The drafter writes a different kind of message depending on the campaign’s purpose. 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 purposes; what shifts is the message’s voice and call-to-action.
Several things compose into the draft prompt, in order:
- Message intent — derived from the campaign’s purpose. Reads as “write a cold sales opener — introduce the product, name one specific thing about the account, 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 purposes.
- Fit criteria + product context — the product’s fit criteria for the campaign’s purpose are rendered into the prompt, so the model knows who the campaign is aimed at.
- Account context + thread history — the account itself, plus the
role_in_campaignoverride if set (see below). - 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 purpose 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 purpose 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 (above the account list) 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 find AI draft directions for this account folded below the composer, 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 a 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.
Per-campaign role override
Section titled “Per-campaign role override”Sometimes the same Account plays different roles in different campaigns. A SaaS company is your client (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 accounts panel on the campaign’s Overview: 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 every relationship type — 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.
Tracked links
Section titled “Tracked links”LeadHunter logs messages, it doesn’t send them — so between “sent” and “replied” 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 (folded below the composer):
- Paste the destination (the field pre-fills with the account’s website) and an optional label, then click Create.
- Copy the short link and paste it into your message wherever you’d have pasted the long URL.
- 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 funnel on Insights → Funnel: a Clicked stage now sits between Initiated and Replied (with its own cohort click-through-rate chart and a column on the per-product / per-campaign tables of Insights → Breakdown), so you can see how much of your outreach gets attention even before anyone replies.
DNC and per-purpose opt-outs
Section titled “DNC and per-purpose opt-outs”Two layers of opt-out apply to logging an outbound message, with or without a campaign:
- Blanket DNC — outbound to a
status='do_not_contact'account is always blocked. 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 — an account can opt out of one kind of outreach and stay open to others: an account that opted out of
salescan still receiveresearchinterview asks orpresspitches. Outbound is blocked when the message’s purpose is in the account’s opt-out list. Under a campaign the purpose is the campaign’s own. Without a campaign, you say which purpose the message serves: when the account has any per-purpose opt-out, the composer shows a purpose picker (the opted-out purposes are disabled) and the message can’t be logged without a choice. Otherwise logging outside a campaign would get around the opt-out. The list is the GDPR / CASL-shaped consent surface, distinct from the blanket flag; it’s kept on the account’s Per-purpose opt-outs panel or throughPOST /api/accounts/{id}/record-dnc/. See Account → Per-purpose opt-outs.
Inbound replies are never blocked — recording what the account sent you isn’t outreach.
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. The rationale: if it’s in the campaign, it cleared the bulk-add filters.
Language resolution
Section titled “Language resolution”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) → EnglishA message logged without a campaign skips the campaign and product steps: the account’s language, then yours, then 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. The app itself is in English; your communication language only decides what you write and read in, so you can work in the English app 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 account’s 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.
Status auto-promotion
Section titled “Status auto-promotion”Logging a message as sent advances the pipeline automatically:
- Outbound sent → campaign-account
outreach_statusbecomessent(orbounced/responded), and the funnel on Insights → Funnel counts the account as initiated. - First outbound on a
prospectaccount → account-levelstatusadvances tocontactedwithsource: campaign_outreachin the audit trail. - Log inbound reply → campaign-account
outreach_statusbecomesresponded; the funnel counts the account as a response, cohort-attributed back to the original initiation date.
Until you log an answer, an account whose latest logged message is their reply shows on Today under Replies waiting (with or without a campaign); see Use Today.
All forward-only. Accounts that are already clients don’t regress when you log a follow-up; a second outbound doesn’t re-fire the prospect → contacted promotion.
These promotions come from a campaign. A message logged with No campaign stays out of every campaign funnel and leaves the account’s status alone; an outbound one still updates the account’s last-contact date, which Client Pulse uses for its contact reminders.
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. Flipping a draft to sent runs the opt-out checks, exactly like logging a new outbound message.
When the account reaches out first
Section titled “When the account reaches out first”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 accounts.
When the first message on such an account is your reply rather than their initial reach-out, log their initial message with Log inbound reply first, then your reply with Type & translate (or AI: continue thread once the thread has an outbound message). To keep the real dates, log the backfilled messages through the API with their sent_at. 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.
Read next
Section titled “Read next”- 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 status the message log moves.