Contacts & CRM
Import contacts, custom fields, labels, deals, and pipelines.
Contacts are the people you reach out to; the CRM tracks what happens after they reply.
Importing
Two routes share the same column-mapping screen: a file upload and an on-demand Google Sheets sync.
From a file (CSV, TSV, or XLSX, up to 50 MB and 50,000 rows; a CSV may use commas or semicolons, read off the header line), the wizard runs Upload, Map columns, Review, Import. Start it from Contacts, or from a campaign's Leads tab to attach the imports to that campaign automatically, or from a segment's member list to pin every row into that segment. The wizard names the target it inherited in its header and on the Review step, and the result confirms it.
- Upload. The file goes up once and is read on the server, which measures every column over the whole file. Nothing is written to your contacts yet. Download a sample file gives you a CSV with every standard column if you are starting from scratch.
- Map columns. Each column shows how full it is across the file, a few of its values, and where it will go. Find a column and Unmapped only help with wide CRM exports, First row is header moves the first row between the headers and the data, and Preview contacts shows the first rows as the contacts they will become, with any row missing a usable address marked.
- Review. The whole file is checked before anything is written, and the result is one line: how many new contacts will be added, how many are already in your workspace, how many repeated rows are merged, and how many rows can't be imported (open it to see which and why). When some contacts already exist, choose Leave as is or Update details for them. Add them to puts everything from the file into segments, labels, and campaigns; the segment picker creates a segment on the spot, and offers one named after the file, so an import becomes an audience in one step. More options holds whether new contacts are subscribed. An import that would be refused as a whole, because it would put the workspace over its plan's contact limit or create more than 100 labels, says so here instead of after the upload, and the import button says exactly what will happen (
Import 1,102 new · update 118). - Import. The import runs on the server in chunks, with live progress, its rate, and the time left. You can close the window: it keeps running, survives a refresh, and everyone in the workspace sees it finish. While it runs, an Importing chip on the Contacts toolbar shows how far it is and reopens it; Stop ends it early and keeps the rows already imported. The result reports imported, updated, skipped, and failed counts, lists the failed rows with their reasons, and Download to fix gives you every failed row as uploaded, under the file's own headers, with the reason in the last column, ready to correct and import again.
A file whose headers match one you imported before maps itself the way you confirmed last time, marked Mapping remembered. The Upload step lists the workspace's recent imports, finished or still running, and opening one shows its progress or result.
You must map at least one column to Email.
| Field | Notes |
|---|---|
| Required, and used to dedupe | |
| First name, Last name, Company, Phone | Standard identity fields |
| Subscribed | yes, true, 1, subscribed and their opposites (no, false, 0, unsubscribed). Applies to new contacts and to existing ones you update; a value the importer cannot read fails only that row |
| Labels | Label names, separated by commas or semicolons. Names you do not have yet are created, up to 100 new names per import |
| Custom field | Anything else: one of your existing custom fields, or a new one you name |
Mapping into your custom fields
The mapping menu lists the custom fields your workspace already has under Your custom fields, most used first, with a search box at the top. Type to filter; type a name nobody has yet and the menu offers to create it. Choosing an existing field writes the column into it, so the values line up with the contacts you already have and every template, filter and segment that reads the field picks them up.
A column whose header names an existing field is mapped to it before you open the menu. The match ignores case, spaces, underscores, dashes and dots, so a company url column lands on your company_url field in its stored spelling instead of starting a second field. Only one column is matched to each field, and standard fields (Email, Company, and so on) are matched first.
When the instance has TypeSafe configured, the columns no header matched are also placed by what their header means: Job Title onto your Title field, Firmenname onto Company, Sector onto Industry. Only a confident answer is applied, each field still goes to one column, and those rows are marked Matched by meaning. Check it. until you change them. It picks names, Company, Phone and your custom fields only: Subscribed and Labels change who gets mail and which labels exist, so they are never guessed. What is sent is the column headers, the kind of value each column holds and your field names, never a cell, and nothing at all unless the first row is clearly headers (no address, date or number in it, and a named Email column); see data control. A column of addresses under any header, or none, is mapped to Email either way.
Each custom mapping says what it will do: Fills your existing field, Creates a new field, or, when the name you typed differs from an existing field only in case or separators, You already have Industry, with Use it to switch to that field. Keep N more as custom fields uses the same matching, so the columns it claims land on your existing fields where it can.
Two columns may fill the same field, which is how a Phone and a Mobile column become one number. Each row names the other column, and where both have a value the one further right is kept; a blank cell never overwrites the other column's value.
Duplicates match on lowercased email, across the workspace
[email protected] and [email protected] are the same contact, and a contact belongs to the workspace, so an address a teammate already added counts as one you have. Choose Leave as is (their details stay untouched) or Update details (empty details are filled in from the file). The API also accepts create_duplicate, which updates too, since one address is one contact per workspace.
Skipping still adds the contact to the campaigns, segments, and labels the import targets: it leaves their fields alone, it does not leave them out of the list. If the same address appears twice in one file it becomes one contact, and the extra rows count as skipped with the line they repeat.
An address you already hold as a contact in another workspace you belong to cannot be added to this one, and that row fails with a reason saying so; the other workspace's contact is never changed by an import here.
One row Warmbly cannot save (a custom field value too large to store, say) fails on its own; the rows imported alongside it are unaffected.
Rows with a missing or invalid email are reported with a line number and reason rather than imported. Addresses are stored lowercased and stripped of any name around them, so a cell reading Dana Reyes <[email protected]> imports as [email protected] and both spellings of one address land on one contact. A blank cell never erases a value you already have, so re-importing a partial export enriches contacts instead of wiping them.
Problems with the mapping itself, an unnamed custom field or a name Warmbly cannot use, are reported once before anything is written, so a single typo can never fail every row.
On the Review step, Campaigns enrols everyone in the file as leads of the campaigns you pick (started from a campaign's Leads tab, that campaign is shown as the fixed target), and Segments pins them into the segments you pick as manual includes, so they stay members whatever the segment's conditions say. A segment created from the picker has no conditions, so it holds exactly the contacts pinned into it. Started from a segment, that segment is always applied and shown as a fixed row; the picker below it adds more.
From Google Sheets, a sheet is a reusable sync source rather than a one-time upload. Connect it once (Warmbly reads only the tab you choose and never writes back), paste the spreadsheet ID from the URL between /d/ and /edit, pick the tab, map columns, then set duplicate handling, a label, and optionally a campaign to enroll into, labels to apply and segments to pin into. Nothing syncs automatically: press Sync now, or save and sync immediately.
Segment targets apply on every run, so a sheet you keep adding rows to keeps feeding the same audience. Opening Sync sources from a segment's member list lists only the sources feeding it and pre-targets a new one to it. A segment deleted later is dropped from the run rather than stopping it.
Saved sources live in your Sync sources list to re-run, edit, or remove. Each sync dedupes on lowercased email using your chosen duplicate handling, so re-syncing a sheet with new rows is safe.
What the review tells you about the list
The Review step and the result also report what the addresses themselves look like: how many were malformed, how many are on known throwaway domains, and how many are shared inboxes like info@. When a quarter or more of the file is malformed or throwaway, both say so.
- Shared inboxes are counted but not held against the list. Mailing
info@is a choice, and plenty of legitimate B2B lists are mostly role addresses. - The import still happens. These are your records, so nothing is refused here. A list bad enough to matter is stopped when you try to launch a campaign with it, which is where the damage would actually occur. See the launch check.
- Lists under 20 rows are not judged, since a share of a handful of rows means nothing.
This is not address verification. It reads the addresses; verification asks the receiving server whether they exist, and runs separately in the background, within a minute of the import. A file that already carries verification results from another service (a status or ZeroBounce Status column, say) is recognised during mapping and offered as Verification status; those verdicts are kept and the background check skips them. See address verification.
Custom fields
For anything beyond identity: industry, plan tier, account owner. Create them by mapping a column to a new custom field during import, or on a contact's Details tab. A later import can map into the same field by picking it from the mapping menu (see Mapping into your custom fields).
Filling them in by hand
New contact (Add lead on a campaign's Leads tab) and a contact's Details tab list your workspace's custom fields as a form: each field is its own input with its name beside it, most used first, so filling one in means typing only the value. The first six are shown and Show N more opens the rest; with more than twelve, a filter box finds one by name. Fields the contact already has are always shown. Clearing a field's value removes it from the contact when you save.
New field adds a field nobody has used yet. As you type its name, your existing fields are suggested, and picking one moves what you typed into that field. A name that matches an existing field, or differs from one only in case or separators, says so with Use it to switch to that field. A name Warmbly cannot use, a value with no name, or the same field filled in twice is flagged before anything is saved. A field whose name was stored before the naming rules above existed is shown on the contacts that have it, but locked: it cannot be changed or removed from the form.
They feed personalization, which is the main reason to get imports right.
Field names may use letters, numbers, underscores, spaces, and dashes: industry, account_owner, Company Mobile, first-name. Other characters (/, #, (, )) are rejected because there would be no way to merge that field into an email. When you map a column to a custom field the wizard fills the name in from the column header and flags it inline if it cannot be used.
Field names with spaces work too
Simple names (letters, numbers, underscores) use a dot: {{.industry}}, {{.account_owner}}. Names with spaces or dashes work the same way, written exactly as named:
Your role as {{.job title}} at {{.Company}} caught my eye.Both forms work everywhere, including inside {{if}} conditions and helper functions.
See Personalization & expressions for the full templating language.
Filtering the list
A filter bar sits above the contact list. Label, Segment, Status and Campaign are always there; open one, tick values, and the list updates immediately with the matching count next to the bar. Add filter adds a custom-field condition (field, contains/is/starts with/ends with, value), a date-added or last-updated range, a number-of-campaigns range, the address verification verdict, the email provider and, on a campaign's Leads tab, lead status and engagement. Each active filter is a pill you can reopen to change or remove with its cross; Clear drops them all, and Save as segment turns the current set into a segment. Free-text search, sort and the column chooser stay in the toolbar.
Free-text search matches first name, last name, email, company and phone. Every word you type has to match one of those, so Test Demo finds the contact whose first name is Test and last name is Demo, and Demo Acme finds everyone named Demo at Acme. Words can be in any order, and only the first six count.
Choosing columns
Columns in the toolbar opens the column chooser. A column of optional details (Phone, Company, a custom field) that nobody in the list you are looking at has filled in steps aside rather than showing a column of dashes, and comes back as soon as one contact has a value; the chooser marks it empty here. Tick a column to show it, untick it to hide it, and drag the grip beside a shown column to change the order. Name is always the first column. Under Custom fields every custom field in the workspace is offered as a column, so a list imported from Apollo or Clay can show the fields you triage on (industry, title, employee count) without opening each contact.
The layout is yours: it is saved to your account for this workspace, so it follows you to another browser, and a teammate looking at the same list keeps their own columns. The campaign Leads tab has its own layout, separate from the contacts page. Reset to default at the bottom of the chooser puts the standard columns back.
Sorting
Click a column header to sort by it, and click it again to flip the direction. Text columns (name, company, phone, custom fields) start ascending; dates and counts start with the newest or largest first. The Sort menu in the toolbar reaches the same orderings, including columns that are hidden or that a phone screen has no room for, email provider, and every custom field in the workspace.
A sort on a custom field orders the values as text. Contacts that do not have the field, or have it blank, are grouped at the end when ascending and at the start when descending. The sort is saved with your layout, so the list opens the way you left it.
Email provider
Each contact's avatar carries a small badge with the logo of whoever hosts their inbox: Google Workspace, Gmail, Microsoft 365, Outlook.com, Yahoo Mail, iCloud Mail, Zoho Mail and the other hosts Warmbly recognises. Hover it for the provider's name. It works for company domains too: [email protected] reads as Google Workspace when Acme's mail is hosted by Google. Warmbly reads it from the domain's mail records in the background, usually within a minute of a contact being added, and fills the list in live. No badge means the check has not reached the contact yet, or the domain has no mail server Warmbly recognises.
A contact on a company domain shows that company's logo in place of their initials, and the Company column shows it beside the company name; when no company is on file, the column shows the domain their address is on. Personal inboxes (Gmail, Outlook.com, Yahoo and the like) keep their initials. Logos are on for Warmbly Cloud and off on a self-hosted instance unless its operator turns them on, because the browser fetches each one from DuckDuckGo; see data control.
It shows on the contacts page and on a campaign's Leads tab alike. You can sort by it from Sort, filter with Add filter > Email provider (including Unknown or not checked yet), select every match and act on them in bulk (add them to a campaign, label them, export them), or save the filter as a segment. Campaign ESP matching uses the same data to pair each lead with a same-provider mailbox.
Selecting rows
Tick a row's checkbox to select it, or the one in the table header to select every row loaded so far. Contact lists load in pages as you scroll, so the header checkbox on its own only ever covers what is on screen.
When more contacts match than are loaded, a bar appears under the header: Select all N matching. Clicking it hands the whole filtered set to the next action, however many pages that is, and the selection bar counts the full number rather than the loaded rows. Unticking a row afterwards takes just that contact out and the count follows. Clear selection in the bar, or the header checkbox, drops back to nothing.
The set is the one the list is showing: search, filters, the subscription facet, and the campaign or segment the list is scoped to all narrow it. Changing any of them clears the selection, because it would no longer mean what it did when you made it. A selection past 250,000 contacts is refused rather than half-applied; add a filter and work through it in parts.
Every bulk action reads the selection: Edit, Segment, Remove from segment, Remove from campaign, Research, Verify, Mark deliverable and Delete. Push to CRM and Research are the exceptions: a push calls the CRM once per contact while you wait, and each research run spends AI credits, so both stay capped at 500 at a time. A workspace in HubSpot mode or Pipedrive mode does not need Push to CRM for that CRM: a contact is created there the first time Warmbly emails it.
Select all matching is in the From contacts picker too, on a campaign's Leads tab and a segment page, so a whole search can be added as leads or members in one step.
Editing one contact
Clicking a row opens the contact's panel. Its Details tab edits the fields the contact is made of: name, email address, company, phone, subscription, campaigns, labels and custom fields. Nothing is sent until Save, Discard puts the panel back to the stored values, and closing it with unsaved edits asks first.
The email address can be changed here, which is the right move when someone's address was mistyped on import or they moved to a new domain. It is stored lowercased and stripped of any display name, and it has to be free: an address another contact already holds is refused, because merging two people's campaign history is not something the edit could undo. A new address also clears the contact's verification verdict and everything the platform had observed about the old mailbox, so the background check starts the new address from scratch on its next pass, and the recipient provider Warmbly matches senders against is worked out again from the new domain. Emails already sent went to the old address and stay in the timeline as they happened.
Editing many contacts at once
Tick rows in any contact list, a segment's members or a campaign's leads, and the selection bar's Edit opens a bulk panel. It can add or remove campaigns, add or remove labels, force a subscription state, and queue custom-field operations (add, edit, delete, rename) that run on every selected contact (the key box suggests your existing fields as you type), including a Select all matching selection that reaches past the loaded pages.
Nothing is applied while you build it up. The header names the list the selection came from, and a Will apply strip above the buttons lists every queued change as a chip you can take back one at a time before pressing Apply. A field operation missing its key, or its value where one is needed, is marked and skipped rather than sent half-finished.
Labels
Colored labels that group and filter contacts (Warm lead, Conference 2026, Enterprise). The same list labels inbox conversations and is what forms file their submissions under. The same picker appears in bulk edit, a contact's Details tab, the new-contact dialog, the filters, and the import and sync wizards.
It supports type-ahead search, and typing an unmatched name offers Create to add and select it in one step. Each label keeps its color everywhere its chip appears.
Labels belong to the workspace, not to whoever made them: every teammate sees the same list and can label contacts with it. Contacts > Labels lists every label with a live contact count. From there you can create one, rename it, change its color, delete it (contacts are kept; the label is removed from them and from inbox threads), or click a row to open the contact list filtered to it.
Labels also drive automation: a sequence can run Add label or Remove label as a contact moves through a flow, so a label can be applied automatically on a positive reply. Add to segment and Remove from segment do the same for segments.
To turn labels and activity into a reusable audience, build a segment: a saved set of conditions over contacts (labels, fields, campaign activity, engagement) that you can browse and add to a campaign in one step.
Where a contact came from
Every contact records its first-touch source: how it entered the workspace, with a detail you would recognise, and when it was first seen. It is shown on the contact's Overview tab and as the first event of its activity timeline, and it never changes afterwards, however the contact is edited or re-imported.
| Source | Set when | Detail |
|---|---|---|
| Manual | Added with Add contact on the Contacts page | |
| Campaign | Added with Add lead on a campaign's Leads tab | The campaign |
| Import | Created by the file import wizard | The file name |
| Sheet sync | Created by a Google Sheets sync source | The sheet |
| API | Created through the API with an API key | The key's name |
| Form | Submitted a hosted form | The form |
| Automation | Written by an automation's Create or update contact action | The automation |
| Remie | Created by Remie on your behalf | |
| Unknown | Created before Warmbly recorded sources |
Only new contacts get a source. An import or API call that matches an existing contact updates its fields and leaves the original source in place, so an address that arrived by hand and later turned up in a file still reads as manual.
A new contact is also an event. contact.created goes to your webhooks and can start an automation with the contact's fields and source, except for bulk arrivals, which stay silent: file imports, sheet syncs, and a single API request adding more than 100 contacts.
Activity timeline
The Activity tab of a contact is one workspace-wide feed, newest first, of everything Warmbly knows about them. Every member with permission to view contacts sees the same timeline, regardless of who created the contact or its campaigns. It includes every campaign email sent, opened, clicked, replied to or bounced (with the campaign, step, subject and sending mailbox), replies with their classified intent, deliverability and suppression events, notes, meetings, and the contact's lifecycle: when it was created and how, and each time it joined or left a campaign or gained or lost a label.
Opens appear once per event, not once per email: a second open from another device is its own row. Opens show the device and mail client they were read on, such as iPhone · Apple Mail app or Gmail · device hidden, and clicks the device and browser the link opened in, with the city and country when known (see How an open was read); expanded, the operating system, browser and full location, and why a device is hidden when a mail provider's image proxy fetched the email. The Overview tab sums this up under How they read: each client and device the contact's opens came from, with the count and the latest. A click names the link: the row reads Clicked Pricing and, expanded, shows the link's text, its full URL, the UTM source, medium, campaign and content it carried, and the browser. Every link in an email is tracked on its own, so two links clicked are two rows. Opens and clicks that came from a machine rather than the person (a mail privacy proxy, a security gateway that follows every link at delivery) carry an auto badge, and the expanded row says which rule caught them; see Link tracking and UTM parameters.
Filter chips narrow the feed (Emails, Replies, Deliv., Notes, Meetings, Campaigns, Lifecycle), the search box matches subjects, campaigns, steps, mailboxes, labels, reasons and, for clicks, the link's text, URL and UTM values, and the date picker bounds it. Each row stays to one line until you click it; expanded, it shows every detail the event carries. The feed updates live as teammates and the schedulers write to it.
At the top of the tab sits the campaign panel: for each campaign the contact is in, its flow with this contact's progress, the lead status, and what the scheduler will do next. See Campaigns for how the next action is worked out and what its states mean.
Deals and pipelines
A pipeline is a named, ordered sequence of stages a deal advances through. On the Pipelines page you create pipelines (new ones start with Open, Qualified, Won), then add, rename, recolor, and delete stages, each showing its deal count. Stage order is the left-to-right column order on the board.
| View | Behavior |
|---|---|
| Table (default) | Every deal across all pipelines, server-paginated. Totals like open count and pipeline value are real sums over the whole result, not just loaded rows |
| Board | Single-pipeline kanban, one column per stage, drag cards to move stages. Each column paginates itself, and header counts come from the server summary |
A deal has a name, stage, value and currency (default USD), expected close date, and a status of open, won, or lost. The linked contact is shown read-only.
Campaigns can create and advance deals automatically with Create deal and Move deal stage actions, so the CRM stays current without manual entry.
When HubSpot is your CRM
In HubSpot mode the deals, pipelines, tasks and notes on these pages are HubSpot's records:
- every change you make is written to HubSpot first, and a change HubSpot refuses is not saved, with HubSpot's reason shown
- pipelines, stages and task types are managed in HubSpot, so editing them here is refused; changes made in HubSpot arrive within minutes
- deals in pipelines that exist only in Warmbly are hidden from the deal table and board until you copy them into HubSpot or switch back
- task types are HubSpot's four (To-do, Call, Email, LinkedIn), and won or lost moves a deal to the matching closed stage
- records written by automations and reply follow-ups reach HubSpot through its sync queue
Each mirrored record links to its HubSpot page.
When Pipedrive is your CRM
Pipedrive mode works the same way on Pipedrive's records, with Pipedrive's model:
- tasks are Pipedrive activities, and task types are your company's activity types
- won and lost are the deal's status, so marking a deal won keeps its stage
- the contact panel shows the person's owner, label and organization, and a label you choose can stop a campaign
Each mirrored record links to its Pipedrive page.
Tasks
CRM > Tasks is the follow-up list for the whole workspace: everything anyone has to do about a contact or a deal, in one place. A task carries a title, a description, a type (Call, Email, Meeting by default, and any type you add), a due date, a priority of low, medium, high or urgent, a status of pending, in progress, completed or cancelled, and an assignee who can be a person, a team, or nobody.
Two views share one list. Table is the flat list with a column per field; By due date groups it into Overdue, Today, Tomorrow, This week, Later and No due date. Both filter, sort and page on the server, so the Overdue, High priority, Pending, Active and Completed totals across the top count the whole matching set rather than the rows loaded so far. The status tabs, the search box, the assignee, team and type facets and the filter popover all narrow the same set.
Clicking a row opens it for editing. Each row carries two controls before its title: the checkbox at the far left selects it for a bulk action, and the square next to the title marks it done on its own.
Selecting many tasks
Tick a row to select it, the checkbox in the table header to select every row loaded so far, or a group's header in the by-due-date view to select that group. When more tasks match than are loaded, a bar appears offering Select all N matching, which hands the whole filtered set to the next action however many pages that is. Unticking a row afterwards takes just that task out and the count follows.
The selection means the filter it was made under, so changing any filter clears it. A selection past 50,000 tasks is refused rather than half applied; add a filter and work through it in parts.
With rows selected, a bar appears at the bottom: Mark done, Status (any of the four), Priority (any of the four), and Delete, which confirms first and names the count. This is how you clear a backlog of tasks nobody needs without opening each one. To clear the follow-ups a workspace collected before it narrowed the setting below, search for out_of_office, tick the header checkbox, take Select all N matching and delete them in one step.
Follow-up tasks from replies
Warmbly can open a task by itself when a reply lands, so a prospect who answers ends up on this list rather than only in the inbox. The task is assigned to the owner of the mailbox that received the reply, due in 24 hours, and titled with the intent and the sender: Follow up: positive reply from [email protected].
Which replies get one is up to you, in Settings > Sending > Reply follow-ups. Every reply is classified first, and the classifier can tell a person from a machine: an out-of-office notice, a helpdesk autoresponder, a bounce and a delivery report are recognized from the message headers, not only from the words in the body, so an auto-responder that never mentions a vacation is still caught.
| Intent | Opens a task by default |
|---|---|
| Positive: interested, wants a call | Yes |
| Question: asked something | Yes |
| Neutral: a human reply we could not bucket | Yes |
| Negative: not interested | Yes |
| Automated: out of office, autoresponders, bounces | No |
Automated replies are off deliberately. A vacation notice is not follow-up work, and a week of sending makes enough of them to bury the real replies. Tick Automated if you do want one per machine reply, or untick the human ones you do not need. Unticking everything is the same as turning the switch off.
The classification does more than decide the task: an automated reply never counts as a reply for stop on reply or for a replied branch in a sequence, and its notification goes to the Out-of-office detected preference rather than New reply, so muting one does not mute the other.
Suppression
A suppressed contact receives no further mail. Unsubscribed contacts are skipped by every campaign automatically; toggle subscription on the Details tab, and filter the list by Subscribed or Unsubscribed.
Suppression also happens automatically when a recipient unsubscribes or replies asking to stop, on a hard bounce or spam complaint, and when a sequence runs an Unsubscribe action. The Suppression list tab holds every entry, takes addresses and whole domains by hand, and lets you lift an entry; a suppressed contact's Overview tab shows why and offers the same.
This is the safe default response to a bad signal: stop sending rather than keep collecting bounces and complaints. See unsubscribe and suppression for the full picture and Deliverability for the signals behind it.