# CRM data hygiene and contact re-validation

> Contact data decays as numbers are disconnected and mailboxes close. Re-check phone and e-mail fields in your CRM on a schedule and before every campaign.

Canonical: https://mobilevalidate.com/use-cases/crm-data-hygiene · Last updated: 2026-09-29

![A phone number and an e-mail address flow into the MobileValidate gateway, which answers valid, invalid or unknown; badges read 42 checks, one API and unknowns are free.](https://mobilevalidate.com/media/library/know-before-you-send-light-1600.webp)

*Check phone numbers and e-mail addresses before you message, call or approve them.*


CRM data hygiene means re-checking the phone numbers and e-mail addresses you already hold, because contact data decays. Numbers are disconnected and reassigned, mailboxes close. Run a bulk job before each campaign and on a schedule: an HLR lookup for numbers, the mailbox check for e-mails. Then suppress dead contacts and fix the rest.

## Why does contact data decay?

A contact record is a snapshot of the day it was collected. After that, life moves on:

- **Numbers are disconnected and reassigned.** People switch operators, drop second SIMs and leave numbers unused until the operator reclaims them. In the United States alone, the FCC estimated that about 35 million numbers are disconnected and made available for reassignment every year ([FCC 18-177, 2018](https://docs.fcc.gov/public/attachments/FCC-18-177A1.pdf)). A reassigned number in your CRM reaches a stranger who never gave you consent. See [reassigned numbers explained](/blog/reassigned-numbers-database-explained).
- **Numbers are ported.** The number still works but sits on a different network, so any stored carrier field is now wrong. See [mobile number portability](/glossary/mobile-number-portability).
- **Mailboxes close.** People abandon personal addresses and lose work addresses when they change jobs. Mail to a closed mailbox [bounces](/glossary/email-bounce), and repeated hard bounces damage your sender reputation.
- **Data entry errors pile up.** Typos, numbers without country codes, and the same person imported twice in different formats.

Decay is invisible until you send. Then it shows up as failed SMS, bounced e-mail, wrong-person complaints and skewed campaign metrics. Checking before sending moves that discovery to a point where it costs a check, not a message and a complaint.

## How does it work?

1. **Export the segment.** Take the contacts you are about to message, or the slice of the database due for its scheduled check. Include the CRM record ID so you can match results back.
2. **Estimate for free.** Call `POST /v1/jobs/estimate` with the numbers, e-mails and checks. It counts valid, invalid, duplicate, cached, unsupported and suppressed rows and gives the maximum cost, without checking or charging anything.
3. **Run a bulk job.** Create it with `POST /v1/jobs` (up to 50,000 numbers and e-mails per job) and pass the estimate's amount as `max_cost`. Add a `default_country` if your CRM stores national formats.
4. **Wait for completion.** Poll `GET /v1/jobs/{id}?wait=30` or subscribe to the `job.completed` [webhook](/docs/webhooks).
5. **Download and import.** Get the CSV (`GET /v1/jobs/{id}/download?format=csv`). It keeps your row order and adds a column per check.
6. **Apply the rules below.** Suppress conclusive negatives, flag temporary problems, correct formats, and write `checked_at` to each record.
7. **Schedule the next run.** Re-check contacts whose last check is older than your chosen interval, and always the segment before a campaign.

The [CSV list cleaner recipe](/docs/recipes/csv-list-cleaner) shows the same flow in code.

## Which checks should you use?

| Check | What it answers | When to use it | Service |
|---|---|---|---|
| `hlr` | Is the mobile number assigned and reachable now? Ported? Current network? | Before SMS campaigns and on a schedule for mobile numbers | [HLR lookup](/services/hlr-lookup) |
| `carrier` | Mobile, fixed line, VoIP or toll-free? Current carrier? | To separate SMS-capable numbers from landlines before a text campaign | [Carrier lookup](/services/carrier-lookup) |
| `mnp` | Was the number ported, and which network is it on now? | To refresh a stored network field cheaply | [MNP lookup](/services/mnp-lookup) |
| `email` | Does the mailbox exist at a major webmail provider? | Before e-mail campaigns, to avoid hard bounces | [E-mail mailbox check](/services/email-verification) |
| `network.carrier_us` | Current carrier for US and Canadian numbers | Bulk jobs on North American records | [US/CA carrier lookup](/services/us-carrier-lookup) |

You don't need every check on every record. A text campaign needs `hlr` (or `carrier` if you only need the line type). An e-mail campaign needs `email`. A full database refresh might run both.

## How often should you re-validate?

There is no single right interval, because decay depends on your audience and on how often you use the data. A practical approach has three layers:

- **Before every campaign**, check the segment you are about to message. This is the check that saves the most, because it runs right before you pay for sends.
- **On a schedule** for the rest of the database, for example every quarter. Prioritize records you haven't messaged or heard from in a long time: silence is often the first sign a contact went stale.
- **On events**, check again when a delivery fails, an e-mail bounces or a customer says "that's not my number". One failure is worth a fresh check with `max_age: 0`, not an immediate deletion.

Store the last `checked_at` per record. Then "re-check anything older than N days" becomes a simple CRM filter.

## Example request

First, a free estimate. The list holds three test numbers, one of which appears twice in different formats, plus two addresses:

```bash
curl https://api.mobilevalidate.com/v1/jobs/estimate \
  -H "Authorization: Bearer $MOBILEVALIDATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"numbers": ["+447700900001", "+447700900002", "07700 900002", "+447700900003"], "emails": ["registered@test.mobilevalidate.com", "not-registered@test.mobilevalidate.com"], "default_country": "GB", "checks": ["hlr", "email"]}'
```

Response (excerpt, test mode):

```json
{
  "object": "estimate",
  "total": 6,
  "valid": 5,
  "invalid": 0,
  "duplicate": 1,
  "cached": 0,
  "unsupported": 0,
  "suppressed": 0,
  "checks": ["number.hlr", "email.valid"],
  "checks_total": 6,
  "billable_max": 0,
  "max_cost": {"amount": "0", "currency": "USD"}
}
```

`07700 900002` is the same number as `+447700900002` once normalized to [E.164](/glossary/e164), so it's marked duplicate and never checked. Test keys are never charged, so the cost is zero here. For a handful of records, for example when a sales rep opens an old contact, the same checks run in real time:

```bash
curl https://api.mobilevalidate.com/v1/lookup \
  -H "Authorization: Bearer $MOBILEVALIDATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"numbers": ["+447700900002"], "emails": ["not-registered@test.mobilevalidate.com"], "checks": ["hlr", "email"]}'
```

Response (excerpt, test mode: the `checks` of the phone row, then of the e-mail row):

```json
[
  {
    "number.hlr": {
      "service": "number.hlr", "status": "completed", "registered": true,
      "attributes": {"status": "unreachable", "ported": false, "roaming": false, "network": "Test Mobile", "mcc_mnc": "23415", "country": "GB"},
      "confidence": "high", "confidence_score": 0.99, "checked_at": "2026-09-28T22:42:26.188Z",
      "cached": false, "age_seconds": 0, "billed": false, "reason": null, "poll_after_ms": null
    }
  },
  {
    "email.valid": {
      "service": "email.valid", "status": "completed", "registered": false, "attributes": null,
      "confidence": "high", "confidence_score": 0.99, "checked_at": "2026-09-28T22:42:26.188Z",
      "cached": false, "age_seconds": 0, "billed": false, "reason": null, "poll_after_ms": null
    }
  }
]
```

The number exists but can't be reached right now: flag it and check again later. The mailbox doesn't exist: stop e-mailing this address and ask for a new one at the next contact.

## What should you do with each result?

| Result | CRM action |
|---|---|
| `number_status: invalid_number` | Fix the format (often a missing country code) or clear the field |
| `number_status: duplicate` | Merge the records |
| HLR `status: reachable` | Keep. Store `checked_at` |
| HLR `status: unreachable` | Flag and re-check before the next send. Don't delete on one answer |
| HLR `status: invalid` | Suppress SMS and calls. The number is not assigned |
| `line_type: fixed_line` | Mark as "no SMS"; use calls or e-mail |
| Mailbox `registered: true` | Keep |
| Mailbox `registered: false` | Suppress e-mail to this address; ask for an update at the next contact |
| Any check `unknown` | Keep the record as it is. Unknown is not negative and is free |

## How much does it cost?

The HLR lookup is $0.005 per number and the MNP lookup $0.001 per number. Other checks, including the mailbox check, have separate real-time and bulk prices on the [pricing](/pricing) page. You're not charged for inconclusive results (unknown, unsupported country, timeout, invalid, duplicate). The free estimate shows the maximum cost before you commit.

Two things keep hygiene cheap. Check only the segment you're about to use, not the whole database every time. And re-running an updated list inside each service's freshness window serves repeat numbers from your account's cache, free.

## What about consent and compliance?

Re-validation is data accuracy, which privacy law expects of you: under the GDPR, personal data must be "accurate and, where necessary, kept up to date" ([GDPR Art. 5(1)(d)](https://eur-lex.europa.eu/eli/reg/2016/679/oj)). Mention contact verification in your privacy notice.

Checks don't create consent. Message only people who opted in, and treat a number that may have been reassigned as a reason to re-confirm consent, not to send. Our [acceptable use policy](/legal/acceptable-use) forbids unsolicited bulk messaging and list building, and checks never return names or profiles. People can object through the [opt-out form](/opt-out); suppressed contacts are never checked or charged.

## What are common mistakes?

- **Checking the whole database once and never again.** Decay is continuous. Check on a schedule and before each campaign.
- **Deleting on one unreachable answer.** A switched-off phone isn't a dead number. Re-check first.
- **Treating unknown as bad.** Unknown means no answer this time, and it's free. Keep the record.
- **Skipping normalization.** Without a country, national numbers can't be checked. Set `default_country` or store E.164.
- **Overwriting the CRM with raw responses.** Store the decision and `checked_at`, not the full JSON.
- **Cleaning only one channel.** A record with a dead mailbox often has a stale phone too. When one field fails, re-check the other before the next campaign on either channel.
- **Ignoring suppression on import.** Numbers and addresses that people asked you to stop using must stay suppressed after a clean-up. Re-importing a cleaned file must not bring them back into campaigns.
- **Assuming a reachable number still belongs to your contact.** Reachable means assigned and live, not "same person". Re-confirm consent when a record is old.

## Frequently asked questions

### Why does CRM contact data go bad?

People change numbers, close mailboxes and leave jobs, and numbers that were given up are reassigned to new people. A record that was right when it was collected can point to nobody, or to someone else, a year later.

### How often should I re-validate contacts?

Before every campaign for the contacts you are about to message, and on a regular schedule for the rest of the database. Contacts you message rarely are the ones most likely to have gone stale. Pick an interval that matches how often you use the data.

### Which checks should I run on CRM records?

The HLR lookup for mobile numbers (assigned and reachable, and the current network), the carrier lookup for line type, and the e-mail mailbox check for addresses at major webmail providers. Start with the free estimate to find invalid and duplicate rows.

### Should I delete a contact when a check says unreachable?

No. Unreachable means the phone couldn't be reached at that moment, for example because it was switched off. Flag it and check again later. Delete or suppress only on repeated conclusive negative answers, such as an unassigned number or a mailbox that doesn't exist.

### Can re-validation tell me whether a number now belongs to someone else?

Not by itself. A check tells you whether a number is assigned and reachable, not who holds it. In the US, the FCC's reassigned numbers database answers whether a number was disconnected after a given date. Re-confirm consent with the person when in doubt.

### Do I pay for duplicates and badly formatted numbers?

No. Duplicates and invalid entries are flagged per row and never checked or charged, and neither are unknown answers. The free estimate counts them before you run anything.

### Can I use re-validation to make an old purchased list usable?

No. Checks clean data you already have a lawful basis to use. They don't create consent, and the acceptable use policy forbids unsolicited bulk messaging.
