
CSV import template for physician contacts (ATS-ready header + NPI dedupe rules)
Ben Argeband, Founder & CEO of Heartbeat.ai — Painfully clear and copy/paste ready.
What’s on this page:
Who this is for
Recruiters and ops teams importing physician contact data into an ATS who need clean records: no duplicate providers, no broken date/boolean fields, and clear provenance (where each row came from). This is built around CSV hygiene, NPI-first identity, and recency tracking so your outreach performance doesn’t quietly decay.
This page is a supporting template. For enrichment after upload, use the primary workflow: upload a physician list for enrichment.
Quick Answer
- Core Answer
- Use an NPI-first CSV with normalized phones/emails, explicit source + refresh date, and strict dedupe keys so your ATS import stays clean and searchable.
- Key Statistic
- Heartbeat observed typicals: include required header and mapping; do not claim native ATS integrations unless true.
- Best For
- Recruiters/ops importing physician contact data into ATS without duplicates/broken fields.
- Primary page for this topic
- https://heartbeat.ai/resources/provider-contact-data/upload-a-physician-list-for-enrichment/
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.
Framework: The “No Mess Imports” Standard: Unique Key → Clean Fields → Log Dates
Most import failures aren’t “CSV problems.” They’re identity problems (no stable unique key), format problems (inconsistent fields), and freshness problems (no way to tell what’s current). The “No Mess Imports” Standard fixes those in order:
- Unique Key: pick one field that represents the provider across systems. For physicians, that’s usually NPI.
- Clean Fields: normalize names, phones, emails, and addresses so your ATS doesn’t split records or reject rows.
- Log Dates: store a refresh date so you can suppress stale contacts and prioritize recent ones.
The trade-off is… you’ll spend a few minutes up front on structure, but you’ll avoid contaminating your ATS with duplicates you can’t unwind later.
Step-by-step method
Step 1) Start with the exact header row (copy/paste)
Paste this header row into row 1 of your CSV. Keep the column names exactly as written to reduce mapping mistakes.
Spreadsheet setup tip: set NPI and postal_code columns to text before you save/export so leading zeros don’t get dropped.
Copy/paste CSV header:
NPI,first_name,last_name,credential,specialty_primary,organization_name,practice_site_name,address_line1,address_line2,city,state,postal_code,country,phone_main,phone_mobile,email_work,email_personal,preferred_contact_channel,do_not_contact,source_system,source_detail,refresh_date,notes
Example row (synthetic):
1234567890,Priya,Shah,MD,Internal Medicine,Northside Medical Group,Northside Clinic – Midtown,123 Main St,Ste 400,Chicago,IL,02110,US,+13125551212,+13125559876,pshah@northside.org,,phone,FALSE,Heartbeat.ai,IM Midwest outreach list,2026-01-05,Prefers calls 12-2pm local
Step 2) Use the field definitions (don’t improvise formats)
Imports break when the same “type” of data shows up in multiple formats (dates, phones, booleans). Use the definitions below and enforce them before you import.
Diagnostic Table:
| Column | Required? | Definition / Allowed values | Example | ATS mapping notes (Bullhorn / Apploi) |
|---|---|---|---|---|
| NPI | Yes | NPI definition: National Provider Identifier; a unique 10-digit identifier for covered health care providers in the U.S. Store as 10 digits, no dashes. | 1234567890 | Map to a custom field if your ATS doesn’t have a native NPI field. Use as your primary dedupe key. |
| first_name | Yes | Given name only. No titles. | Priya | Standard candidate/contact first name. |
| last_name | Yes | Family name only. No suffixes unless your ATS requires them. | Shah | Standard candidate/contact last name. |
| credential | No | Short credential string. | MD | Often maps to “Suffix” or “Credential” custom field. |
| specialty_primary | No | One specialty only (avoid comma-separated lists). | Internal Medicine | Map to specialty field or tags. Keep one value for filtering. |
| organization_name | No | Employer/health system/group name. | Northside Medical Group | Map to “Company” or “Employer” field if available. |
| practice_site_name | No | Clinic/site label (optional). | Northside Clinic – Midtown | Useful for multi-site groups; map to location/site custom field. |
| address_line1 | No | Street address line 1. | 123 Main St | Map to address fields if your ATS supports them; otherwise store in notes. |
| address_line2 | No | Suite/unit. | Ste 400 | Same as above. |
| city | No | City. | Chicago | Standard address mapping. |
| state | No | 2-letter state code if U.S. | IL | Standard address mapping. |
| postal_code | No | ZIP or postal code as text (preserve leading zeros). | 02110 | Import as text, not number. |
| country | No | Country name or ISO code; be consistent. | US | Optional unless you recruit internationally. |
| phone_main | No | Primary office line. Store as E.164 if possible (+1XXXXXXXXXX). If not, digits only. | +13125551212 | Map to “Phone” or “Work Phone.” Avoid mixing extensions into the number field. |
| phone_mobile | No | Mobile number (E.164 preferred). Keep separate from office line. | +13125559876 | Map to “Mobile Phone” if available; otherwise custom field. Heartbeat.ai can use ranked mobile numbers by answer probability. |
| email_work | No | Work email. Lowercase. One email per field. | pshah@northside.org | Map to primary email if you prioritize work outreach. |
| email_personal | No | Personal email (only if you have a legitimate recruiting purpose and honor opt-outs). Lowercase. | priya.shah@gmail.com | Consider mapping to secondary email/custom field; apply suppression rules. |
| preferred_contact_channel | No | One of: phone,email,unknown | phone | Map to a tag or custom field; helps routing and cadence selection. |
| do_not_contact | No | Boolean: TRUE or FALSE (uppercase). Default FALSE. | FALSE | Map to your ATS DNC/opt-out field if available. Never overwrite TRUE with FALSE. |
| source_system | No | Where the row came from (system name). | Heartbeat.ai | Map to “Source” or custom field for auditability. |
| source_detail | No | Free text: list name, campaign, or query used. If it includes commas, ensure proper CSV quoting. | IM Midwest outreach list | Store for traceability when complaints or bounces happen. |
| refresh_date | Yes | Recency definition: the date you last verified/updated the contact row. Use ISO format YYYY-MM-DD. | 2026-01-05 | Map to “Last Updated” custom field. Use it to suppress stale contacts. |
| notes | No | Short notes. If notes include commas, ensure your CSV exporter properly quotes the field. | Prefers calls 12-2pm local | Map to notes/comments field. |
Step 3) Set your dedupe policy before you import
Import failures come from inconsistent fields and missing unique keys. For physicians, NPI is the best primary key. Emails and phones are volatile: people change employers, domains change, and numbers get reassigned. Your dedupe policy should reflect that reality.
- Primary dedupe key: NPI
- Secondary match signals (for review, not auto-merge): last_name + first_name + state, or email_work
- Never auto-merge solely on phone or email (they can be shared, forwarded, or recycled)
For a deeper walkthrough, link this into your SOP: how to dedupe a provider list by NPI.
Step 4) Map fields into Bullhorn / Apploi (pilot first)
Two rules keep your ATS usable:
- Don’t overload one field (for example, multiple emails in one email field). Keep one value per field.
- Keep structured data structured (dates as YYYY-MM-DD, booleans as TRUE/FALSE, state as 2-letter code).
Do a 10-row pilot import, verify how Bullhorn/Apploi stored each field, then import the full file.
| CSV column | Bullhorn target | Apploi target | Field type | Common failure mode |
|---|---|---|---|---|
| NPI | Custom text field (e.g., NPI__c) | Custom field (text) | Text | Imported as number and loses leading zeros (treat as text) |
| refresh_date | Custom date field | Custom field (date) | Date | Rejected due to non-ISO formats (use YYYY-MM-DD) |
| do_not_contact | DNC/opt-out field (or custom boolean) | Opt-out field (or custom boolean) | Boolean | TRUE/FALSE mismatch (don’t use Yes/No) |
| phone_main | Phone (work) | Phone | Text | Extensions included in number field (put ext in notes) |
| phone_mobile | Mobile phone (or custom) | Mobile phone (or custom) | Text | Mobile overwritten by office line due to single phone mapping |
| email_work | Text | Multiple emails in one cell causing parse issues | ||
| source_system | Source/custom | Source/custom | Text | Missing provenance makes complaints hard to investigate |
If you’re using Heartbeat.ai workflows, you can also start from your existing file and upload it for enrichment: upload your file.
Step 5) Run a pre-import validation pass
- Check NPI format: 10 digits, no blanks for rows you intend to dedupe reliably.
- Normalize emails: lowercase; remove spaces; one email per field.
- Normalize phones: pick E.164 (+1…) or digits-only and stick to it.
- Validate refresh_date: ISO YYYY-MM-DD only.
- Confirm do_not_contact: TRUE/FALSE only; never overwrite TRUE with FALSE on re-import.
Weighted Checklist:
Use this as a go/no-go gate before you import. Score each item 0 (missing), 1 (partial), 2 (clean). If you’re under 12/16, fix the file first.
- (2) NPI present and 10-digit for the rows you plan to keep long-term.
- (2) Dedupe rule written: “NPI = unique; never auto-merge on phone/email.”
- (2) refresh_date populated for every row (YYYY-MM-DD).
- (2) Phone split: office vs mobile in separate columns.
- (2) Email split: work vs personal in separate columns.
- (2) do_not_contact respected: TRUE never overwritten by FALSE on re-import.
- (2) Source traceability: source_system + source_detail filled in.
- (2) ATS field mapping tested with a 10-row pilot import and rollback plan.
Common pitfalls
Pitfall 1: Treating email/phone as the unique key
Emails and phones change. If you dedupe on them, you’ll either merge the wrong people or create duplicates when a physician changes systems. Use NPI as the stable identity key and treat contact points as attributes that can rotate.
Pitfall 2: No refresh_date (you can’t tell what’s stale)
If you don’t store recency, you can’t suppress old contacts, and your team will waste cycles on dead emails and wrong numbers. Add refresh_date to every row and keep it updated when you re-verify.
Pitfall 3: Multi-value fields that break parsing
Comma-separated specialties, multiple emails in one cell, or “(312) 555-1212 ext 9” inside a phone field will cause import errors or make your ATS search unreliable. Keep one value per field and store extensions/notes separately.
Pitfall 4: Importing the full file without a pilot
The fastest way to create a cleanup project is to import the full file before you confirm mapping. Do a 10-row pilot, verify how your ATS stored each field, then import the full file.
Pitfall 5: Overwriting opt-outs on re-import
If your ATS has a do-not-contact flag, treat it as write-protected. Your import process should never flip TRUE back to FALSE because a source file didn’t carry the opt-out forward.
Import error decoder (symptom → cause → fix)
| Symptom | Likely cause | Fix |
|---|---|---|
| refresh_date rejected | Date not in YYYY-MM-DD | Convert to ISO format; ensure the column is mapped as a date field |
| ZIP/postal_code loses leading zeros | Spreadsheet saved it as a number | Set the column to text before saving; keep postal_code as text in the ATS |
| Duplicates created on re-import | No match key (or ATS match not configured) | Match/update on NPI; quarantine rows missing NPI for review |
| Phone fields look scrambled | Extensions or multiple numbers in one cell | Keep one number per field; put extensions in notes |
| Emails bounce immediately | Stale contacts or typos/spaces | Lowercase + trim; use refresh_date to suppress older rows until re-verified |
How to improve results
CSV_TEMPLATE worksheet: import-ready row validator
This is the fastest way to keep your ATS clean while still moving quickly. Create a second tab in your spreadsheet called QUARANTINE and apply these rules before every import:
- Rule A (identity): If NPI is blank or not exactly 10 digits, move the row to QUARANTINE (do not import into the main ATS table).
- Rule B (freshness): If refresh_date is blank or not YYYY-MM-DD, move to QUARANTINE.
- Rule C (opt-out safety): If do_not_contact is TRUE, keep the row but ensure your import cannot overwrite TRUE with FALSE.
- Rule D (contact hygiene): If email_work or email_personal contains spaces or multiple emails, split into separate fields or move to QUARANTINE.
- Rule E (phone hygiene): If phone fields contain “ext”, “x”, or multiple numbers, move to QUARANTINE and store the extension in notes.
Dedupe decision tree:
- If NPI matches an existing record → update contact attributes (phones/emails), update refresh_date, preserve do_not_contact if TRUE.
- If NPI does not match → create a new record.
- If NPI missing → do not auto-merge; quarantine for review.
If your ATS can’t update records by NPI
Some ATS setups can import but can’t reliably update existing records based on a custom key. In that case, use a staging workflow:
- Import the CSV into a staging list/table (or a temporary project) instead of your main database.
- Match staging rows to existing records using NPI.
- Apply updates (phones/emails/refresh_date/source fields) to the matched records.
- Create new records only for NPIs that don’t exist yet.
Track the right metrics (and define them consistently)
Measure this by… running a weekly scorecard on a fixed cohort (for example, the last imported batch) and comparing outcomes by refresh_date band and channel.
- Deliverability Rate = delivered emails / sent emails (per 100 sent emails).
- Bounce Rate = bounced emails / sent emails (per 100 sent emails).
- Connect Rate = connected calls / total dials (per 100 dials).
- Answer Rate = human answers / connected calls (per 100 connected calls).
- Reply Rate = replies / delivered emails (per 100 delivered emails).
Measurement instructions:
- Tag every imported record with source_system, source_detail, and refresh_date.
- Run outreach in a consistent cadence for 7–14 days (same number of touches per record).
- Export outcomes (delivered, bounced, replies; dials, connected, human answers) and compute the rates above using the stated denominators.
- Compare performance for records refreshed recently vs. older refresh_date values to decide when to re-verify or suppress.
Suppression SOP (so you don’t re-contact opt-outs)
Keep opt-outs in two places: your ATS record (do_not_contact = TRUE) and a suppression list used by your outreach tools. On every re-import, treat TRUE as write-protected and never overwrite it with FALSE.
Use NPI to keep your ATS clean across re-imports
If you’re building lists repeatedly, don’t rely on ATS “duplicate detection” defaults. Make NPI the anchor, then update contact attributes (phones/emails) as they change. If you need the upstream list-building workflow, use: how to build a physician call list.
Outreach Templates:
These templates assume you have NPI, specialty, and a recent refresh_date. Keep them short and operational.
Template 1: Phone opener (office line)
- You: “Hi, this is [Name]. I’m trying to reach Dr. [Last Name] about a physician opportunity. What’s the best way to get a message through—direct line, email, or a good time window?”
- If asked details: “It’s a [specialty] role with [high-level location/setting]. I can send a 3-line summary—what email should I use?”
Template 2: Email (work email)
- Subject: Dr. [Last Name] — quick question
- Body: “Dr. [Last Name], I recruit physicians for [organization/type]. Are you open to a brief call about a [specialty] role in [location]? If not you, who’s best to contact? — [Name], [Phone]”
Template 3: Follow-up (after no response)
- Body: “Circling back, Dr. [Last Name]. If timing’s bad, I can send details and close the loop. Preferred channel: phone or email?”
Ops note: tie each outreach attempt back to refresh_date so you can see whether stale records are dragging down deliverability and connect rates.
Legal and ethical use
Use physician contact data for legitimate recruiting outreach with a clear business purpose. Respect opt-outs immediately, keep suppression lists, and follow applicable privacy and communications laws in the jurisdictions you operate in. If you’re unsure about a specific rule (texting, calling hours, email compliance), get counsel—this article is operational guidance, not legal advice.
Evidence and trust notes
We treat provider identity and contact hygiene as an operations problem: stable keys, clean fields, and logged recency. For how we evaluate data quality and sourcing practices, see our methodology: trust methodology.
External reference: NPI is defined and maintained via the NPPES system: https://nppes.cms.hhs.gov/.
FAQs
What’s the minimum set of columns I need for a clean import?
At minimum: NPI, first_name, last_name, and refresh_date. Then add at least one contact channel (phone_main or email_work) plus source_system for traceability.
Why is NPI the best dedupe key for physicians?
NPI is designed as a unique provider identifier. Phones and emails are attributes that change; NPI is the stable identity anchor for dedupe and record updates.
How should I format phone numbers in the CSV?
Pick one standard and enforce it. E.164 (+1XXXXXXXXXX) is the cleanest for systems that support it. Otherwise use digits-only consistently and keep extensions out of the number field.
What should I do when two rows share the same NPI but different phones/emails?
Keep one provider record (by NPI) and treat phones/emails as updatable attributes. Preserve the most recent refresh_date and store older contact points in notes or secondary fields if your ATS supports it.
How do I avoid breaking my ATS with duplicates on re-import?
Write the rule: NPI is unique. Import updates should match on NPI and update fields without creating a new record. Pilot with 10 rows, confirm behavior, then run the full import.
Next steps
- If your goal is enrichment after upload, use the primary workflow: upload a physician list for enrichment.
- If you already have a file, run the CSV_TEMPLATE validator above and then upload your CSV for enrichment.
- If you want to test the workflow end-to-end, start free search & preview data and export using the header above.
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.