Back to Heartbeat Blog

How to Build a Physician Call List (CSV Template, Dedupe, Validate, Route)

0
(0)
August 31, 2026

What’s on this page:

0
(0)

Last updated: August 31, 2026

54322

By Ben Argeband, Founder & CEO of Heartbeat.ai — the short version: get the columns right, get the order right, and know what to do when a call fails.

What’s on this page:

Who this is for

Recruiters and ops teams building physician call lists who are tired of dead numbers, duplicates, and wrong-person calls. If speed-to-submittal, connect rate, and clean tracking matter to you, this workflow is built for that.

Quick answer

Core answer
To build a physician call list, standardize a CSV, dedupe by NPI/license, validate phones, route by line type, then track call blocks and refresh after no-answer clusters (same source plus same phone_validation_date cohort producing repeated no_answer outcomes).
Best for
Recruiters and ops teams building physician call lists who are tired of dead numbers and duplicates.

Compliance & safety

This method is for legitimate recruiting outreach only. Always respect candidate privacy, opt-out requests, and local data laws. Heartbeat does not provide medical advice or legal counsel.

The system: dedupe, validate, rank, route, track

A physician call list isn’t a database export. It’s a working queue built to produce connects and qualified conversations without burning goodwill.

  • Dedupe: collapse the same physician across sources using stable identifiers (NPI/license) so you don’t call the same person twice.
  • Validate: run phone validation so disconnected or invalid numbers stop clogging your dial blocks.
  • Rank: prioritize the best numbers first, based on line type, validation recency, and prior outcomes.
  • Route: decide what to dial first based on line type (mobile vs. direct dial vs. office line) and your compliance posture.
  • Track: log outcomes in a call block tracker so you can refresh intelligently after no-answer clusters instead of guessing.

Workflow summary

  • Start with a strict CSV header (below) so every row is dial-ready.
  • Dedupe by NPI/license, not by name.
  • Validate phones, then route by line type.
  • Dial in call blocks, then refresh the slices that decay.

Step-by-step method

Step 1: Define the list’s job so “done” is measurable

Before you build anything, define what the list is supposed to do:

  • Use case: first-touch sourcing, reactivation, credentialing follow-up, or interview scheduling.
  • Owner: who updates outcomes (recruiter) vs. who maintains hygiene (ops).
  • Cadence rule: how many attempts per number before you pause and refresh validation or switch numbers.

Quality beats size here. Reducing duplicates and wrong-person calls usually improves throughput faster than adding more rows to the list.

Step 2: Start from a strict CSV template so ops can enforce hygiene

Use one CSV template across the team. Without it, you can’t dedupe, route, or measure consistently.

Copy/paste CSV header line (recommended order)

physician_full_name,npi,license_state,license_number,specialty,practice_name,phone_number,line_type,phone_validation_status,phone_validation_date,source,priority_score,owner,call_block_id,attempt_count,last_call_outcome,last_call_date,opt_out_status,notes

CSV header reference table

Column Purpose Required? Notes / allowed values
physician_full_name Human-readable identity Yes As displayed in outreach
npi Primary dedupe key Strongly recommended 10-digit NPI
license_state Dedupe + compliance context Recommended Two-letter state code
license_number Secondary dedupe key Recommended As provided by source
specialty Routing + messaging Recommended Normalize to your taxonomy
practice_name Context for office lines Optional Useful for gatekeeper calls
phone_number Dial target Yes E.164 preferred (+1XXXXXXXXXX)
line_type Routing decision Yes mobile | direct_dial | office | unknown
phone_validation_status Hygiene gate Yes valid | invalid | unknown
phone_validation_date Recency Recommended YYYY-MM-DD
source Traceability Yes Where the row came from
priority_score Rank order Recommended Pick one system: 0–100 or A/B/C
owner Assignment Recommended Recruiter name or email
call_block_id Batch tracking Yes Example: Week1-AM-Mobile-SourceA
attempt_count Cadence control Yes Integer
last_call_outcome Next action Yes no_answer | voicemail | gatekeeper | wrong_person | connected | do_not_contact
last_call_date Recency Yes YYYY-MM-DD
opt_out_status Suppression Yes active | opted_out
notes Human context Optional Keep short; no sensitive data

If you want a faster import path, use upload a CSV to map physician contacts and keep the same header going forward.

Step 3: Dedupe using stable identifiers, not names

Names collide and practices change. Your dedupe keys need to be stable:

  • Primary key: NPI, best for collapsing multiple sources into one physician record.
  • Secondary key: license_state plus license_number, useful when NPI is missing or inconsistent.

Operationally, create a physician_id in your system that is either the NPI or a deterministic ID built from license_state and license_number when NPI is absent. If you need help matching identifiers, see NPI and license matching for dedupe.

  • Precedence rule: when NPI is present and passes your checks, treat it as the primary identity key.
  • Conflict rule: if NPI and license disagree for the same physician_full_name, quarantine the row for review and don’t dial until it’s resolved.

The trade-off is real: strict dedupe reduces your row count, but it builds recruiter trust because you stop double-tapping the same physician across different sources and numbers.

Step 4: Validate phones before they hit a dial block

Phone numbers are volatile. Skip validation and you’ll waste blocks on disconnected numbers while inflating your no_answer noise. Run phone validation and store both status and date so you can enforce recency.

  • Gate rule: only dial numbers with phone_validation_status = valid, or explicitly allow unknown for a controlled test block.
  • Refresh rule: if a block shows clustered no_answer outcomes, refresh validation before retrying the same stale slice.

Validation is a workflow step, not a one-time enrichment. See phone validation for provider direct dials.

Step 5: Route by line type so you don’t burn time on the wrong path

Not all numbers behave the same. Your list needs a line_type field and a routing rule. Mix office lines with mobile in the same workflow and your connect expectations and scripts will drift.

Align definitions using office line vs direct dial vs mobile.

  • Handling unknown: route unknown line_type into a separate test call block until it’s classified.
Line type Dial goal Best window Recommended opener When to switch/refresh
mobile Direct connect with physician Short windows; avoid obvious clinic rush Permission-based: \”Okay time for a 20-second question?\” If repeated no_answer in the same source + phone_validation_date cohort, refresh validation or try alternate number
direct_dial Physician or close assistant Business hours Professional: identity + reason + quick ask If wrong_person, audit mapping and dedupe keys
office Best callback path (not a full pitch) Office hours Gatekeeper-respectful: \”best direct path for a brief message?\” If gatekeeper-heavy, separate into its own call block and adjust script
unknown Classify the number Controlled test block Short identity check After first outcome, set line_type and route accordingly

Step 6: Rank the queue so the first hour produces the most connects

Ranking is where speed-to-submittal shows up. Don’t randomize. Rank by:

  • line_type (mobile → direct_dial → office → unknown)
  • validation recency (newer first)
  • prior outcomes (connected > voicemail > no_answer)
  • time-of-day fit (office lines during office hours; keep mobile attempts tight and respectful)

Step 7: Track outcomes with a call block tracker so ops can fix the list

You need a batch concept so you can diagnose list issues quickly. That’s what a call block is.

Metric definitions to use consistently

  • Connect Rate = connected calls divided by total dials, reported per 100 dials.
  • Call block = a defined batch of dials (a time window plus a specific list slice) used to track outcomes and compare performance across sources, line types, and validation recency.

Minimum tracking fields per dial attempt:

  • call_block_id
  • dial_timestamp
  • phone_number
  • line_type
  • outcome (connected / voicemail / no_answer / wrong_person / gatekeeper / do_not_contact)
  • next_action (retry, switch number, refresh validation, suppress)

Diagnostic table

Symptom in your call block Likely root cause Fast test Fix in the list
High wrong_person outcomes Bad dedupe; mismatched physician-to-number mapping Sample rows; verify NPI + name + practice alignment Enforce NPI/license dedupe; store source and last_verified fields
Lots of no_answer clustered in one source slice Stale phones; validation recency too old Re-validate that slice; compare outcomes before/after Require phone_validation_date; refresh after no-answer clusters
Gatekeeper-heavy blocks Too many office lines routed as primary Split block by line_type; compare Connect Rate per 100 dials Route mobile/direct_dial first; office lines in separate blocks
Recruiters cherry-pick and ignore the queue Ranking not trusted or not explainable Ask: \”Why is row #1 first?\” If no one can answer, it fails Make priority_score rules explicit; include validation recency and outcomes
Duplicate calls to same physician across recruiters No shared physician_id; no ownership/locking Find same NPI appearing in multiple owners in the same week Add physician_id + owner + last_call_date; enforce assignment rules

Weighted checklist

Use this to decide whether a list is dial-ready. Score each item 0–2 (0 = missing, 1 = partial, 2 = solid), multiply by weight, then total.

Item Weight What \”2 points\” looks like
Dedupe keys present (NPI and/or license) 5 Most rows have NPI or license_state+license_number; duplicates collapsed
Phone validation fields present 5 phone_validation_status + phone_validation_date populated; invalid suppressed
Line type populated 4 mobile/direct_dial/office/unknown filled; routing rules documented
Suppression ready (opt-out) 5 opt_out_status enforced; do_not_contact outcomes immediately suppress
Call block tracking fields 4 call_block_id, attempt_count, last_call_outcome, last_call_date required
Source traceability 3 source field standardized so ops can isolate bad feeds
Ranking logic documented 3 priority_score rules are explainable and consistent across owners

Outreach templates

Phone-first openers designed for physician recruiting. Keep them short, confirm identity, and offer an easy opt-out.

Template 1: Mobile first-touch (identity + permission)

Opener: “Hi Dr. [Last Name]—this is [Name]. Quick check: did I catch you at an okay time for a 20-second question?”

If yes: “I recruit physicians for [Organization/Client]. Are you open to hearing about roles, or would you prefer I don’t reach out again?”

If no: “No problem—what’s a better time, or should I email instead?”

Template 2: Office line (gatekeeper-respectful)

“Hi—can you help me reach Dr. [Last Name]? I’m calling about a professional opportunity. What’s the best direct path for a brief message—direct line, voicemail, or email?”

Template 3: Voicemail (low detail, clear callback and opt-out)

“Dr. [Last Name], this is [Name] at [Company]. I’m calling about a physician opportunity. If you’d like details, call me at [Number]. If you prefer I don’t contact you again, tell me and I’ll opt you out.”

Common pitfalls

1. Treating more rows as progress

Big lists feel productive until your team spends the week dialing duplicates and wrong-person numbers. Dedupe and validation usually move outcomes faster than adding volume.

2. No stable dedupe key

If you dedupe on name plus city, you will double-call physicians and miss merges. Use NPI/license matching as the backbone, then keep names as display fields only.

3. Mixing line types in the same block without routing rules

Office lines and mobile behave differently. Blend them and your Connect Rate per 100 dials becomes hard to interpret, and recruiters lose trust in the queue.

4. Not refreshing after no-answer clusters

When a slice of the list produces repeated no_answer outcomes, don’t just increase attempts. Refresh validation and/or swap to a different number for that physician. Phone numbers decay; your process has to assume it will happen.

5. Building phone-centric rows instead of physician-centric rows

A common mistake: teams build a CSV where each row is “a phone number” rather than “a physician contact attempt.” That makes it hard to dedupe, hard to suppress correctly, and hard to route by line type. Fix it by making the CSV physician-centric (NPI/license plus physician_id) and treating phone_number as an attribute with its own validation and line type.

How to improve results

Build a measurement loop ops can act on

Run weekly call blocks that are intentionally comparable — same time window, same routing rules — then split results by source, line_type, and validation recency.

  • Connect Rate = connected calls divided by total dials, reported per 100 dials.
  • Connects per hour = dials per hour multiplied by Connect Rate. Use your own observed dials per hour and your own Connect Rate from comparable call blocks.

There’s no single universal benchmark here, because your baseline depends on mix: line type, specialty, time window, and list hygiene all shift the number. The point is consistency — comparable call blocks make improvements obvious.

Refresh triggers instead of fixed schedules

  • If a source slice’s Connect Rate drops across two comparable call blocks, re-validate that slice.
  • If wrong_person outcomes rise, audit dedupe keys and mapping rules (NPI/license alignment).
  • If gatekeeper outcomes dominate, separate office lines into their own call blocks and adjust scripts.

Suppression and consent handling

Make opt-out and do_not_contact outcomes irreversible in the workflow. Your list should suppress across all sources and future imports. Track consent signals where applicable, and don’t re-add suppressed contacts just because a new source file appears.

Make it easy to start and hard to break

  • Lock required columns in your CSV template.
  • Reject imports missing phone_number or opt_out_status; quarantine imports missing NPI/license or phone_validation fields for cleanup before dialing.
  • Keep a single owner for list hygiene (ops) and a single owner for outcomes (recruiting).

If you want to move fast, you can start free search and preview data — access plus refresh, verification, and suppression, not a static list — and then export into the template once your routing fields are in place.

Legal and ethical use

This playbook is about legitimate recruiting outreach. Build your process around respect and suppression:

  • Honor opt-out requests immediately and globally, across all lists and sources.
  • Be transparent about who you are and why you’re calling.
  • Keep notes professional; avoid storing sensitive personal data.
  • Follow applicable phone/SMS rules and local data laws. For baseline U.S. guidance, review the TCPA overview from the FCC: Telephone Consumer Protection Act (TCPA).

Heartbeat.ai does not provide legal counsel. If you have edge cases — multi-state outreach, texting policies, consent language — get your counsel to bless the workflow before you scale it.

Evidence and trust notes

This guide is designed to be auditable: you can trace each row to a source, see validation recency, and compare outcomes by call block. For how Heartbeat approaches data quality, verification, and suppression, see our Trust Methodology.

Related implementation resource: CSV import template for physician contacts.

FAQs

What columns do I need to build a physician call list that recruiters will actually use?

At minimum: physician name, NPI (or license_state + license_number), phone_number, line_type, phone_validation_status + date, source, call_block_id, attempt_count, last_call_outcome/date, and opt_out_status.

How do I dedupe physicians across multiple sources without losing good numbers?

Dedupe at the physician level using NPI/license, then keep multiple phone numbers as separate dial targets tied to the same physician_id with their own validation status, line type, and outcomes.

How often should I refresh phone validation?

Use triggers: refresh when you see no-answer clusters (same source plus same phone_validation_date cohort producing repeated no_answer outcomes) or when a source slice’s Connect Rate drops across comparable call blocks.

Should I dial office lines and mobile numbers in the same block?

Usually no. Separate blocks by line_type so your metrics are interpretable and your scripts match the channel. If you must mix, tag line_type and report Connect Rate per 100 dials by line_type.

What’s the fastest way to get a list into Heartbeat.ai?

Use a clean CSV template and map fields once, then keep the same header going forward. You can upload your file here to start mapping.

Next steps

  • Implement the CSV header line above and enforce required fields (NPI/license, validation, line type, suppression).
  • Run one controlled call block and split results by source and line_type to find the fastest fixes.
  • Align your team on definitions and routing using line type guidance and NPI/license matching.
  • When you’re ready to operationalize, create a Heartbeat.ai account and start free search and preview data.

About the Author

Ben Argeband is the Founder and CEO of Swordfish.ai and Heartbeat.ai. With deep expertise in data and SaaS, he has built two successful platforms trusted by over 50,000 sales and recruitment professionals. Ben’s mission is to help teams find direct contact information for hard-to-reach professionals and decision-makers, providing the shortest route to their next win. Connect with Ben on LinkedIn.

Access 11m+ Healthcare Candidates Directly Heartbeat Try for free arrow-button