Skip to content

Contact Enrichment Waterfall: Quality and Privacy

By Shuk Huay Koh, Founder, Mir Tech

Published Updated 6 min read

Data EnrichmentData QualityPrivacyCRM
On this page

Enrichment fills gaps in your CRM: company size, industry, job title, location. Done well, it makes scoring, routing and outreach more accurate. Done badly, it buries good data under wrong data and creates a privacy problem you did not have before.

This page is a reference for the whole loop: the CRM triggers enrichment, a warehouse holds the working data, a waterfall of providers fills the gaps, and validated values go back. You need CRM admin access, a warehouse, a scheduler or serverless function, and an account with at least one data provider.

Enrich fewer fields than you think you need

Every extra field adds cost, a chance of error and personal data you must justify holding. Work backward from use. If routing needs country and company size, and scoring needs job level, those three are your fields. The lead routing guide and the lead scoring pipeline show what each system reads.

Give every field a one-line standard before you buy anything.

DecideExample
FormatISO country codes; a fixed list of job levels
Winner when providers disagreeProvider A for title, provider B for company size
Maximum age before refreshTitle: 6 months. Country: 2 years
Human editsNever overwritten

The last row prevents a common failure. A rep corrects a title, and the next batch job silently puts the old one back.

Privacy rules by region

Enrichment means collecting personal data about people who did not give it to you directly. The rules depend on where your contacts live.

RegionWhat appliesSource
EU and UK (GDPR)You need a lawful basis. Legitimate interests applies only where the individual's rights do not override it, so weigh the two and write down the result. Because the data does not come from the person, you must tell them who you are, why you hold the data, where it came from and what rights they have. The deadline is a reasonable period and at most one month, or your first contact if that comes sooner.Article 6, Article 14
Malaysia (PDPA)The 2024 amendments bind data processors directly to the security principle, make breach notification to the Commissioner mandatory, and require controllers and processors to appoint a data protection officer.DFDL summary

Mir Tech is based in Malaysia, so we watch the PDPA closely. Check it with your own counsel for your situation. This is general guidance, not legal advice.

Four rules hold in every region. Record which provider supplied which value and when. Set a retention period and delete what you no longer need. Honor opt-outs everywhere, including the warehouse copy, with a suppression list that the pipeline checks before each call. And do not enrich contacts in regions where you have no lawful basis and no plan to contact them. Ask providers how they source their data and whether individuals were informed.

A lighter setup for small teams

If you enrich a few hundred contacts a month, skip the warehouse. A CRM-native enrichment feature, or one provider connected through Zapier or Make, covers most of the value. Keep the rules: enrich only the fields you use, record the source of each value, check the suppression list first, and never overwrite a human edit. Move to the pipeline below when volume, cost or audit needs grow.

Components

You need a data provider, or several, with documented REST APIs and clear terms on how they source data. Product lineups change often, so confirm current availability before you commit. You also need a warehouse such as Snowflake, BigQuery or Redshift with a dedicated schema, CRM API access (a connected app with OAuth scopes in Salesforce, or a private app with contact scopes in HubSpot), a scheduler or serverless function such as AWS Lambda on a schedule, and a secrets manager.

Never put API keys in code or in the repository. AWS Secrets Manager replaces hard-coded credentials with a run-time lookup and supports automatic rotation. Google Secret Manager does the same on Google Cloud. Give the job a role that can read only the secrets it needs.

Staging table

Create a schema just for enrichment. A staging table holds contacts waiting to be processed:

create table enrichment_staging (
  record_id     varchar,
  crm_id        varchar not null,
  email_address varchar,
  status        varchar default 'pending',
  attempts      int default 0,
  processed_at  timestamp_ntz
);

Keep a status column and do not delete rows after success. It gives you a retry queue and an audit trail. For cheap, short-lived storage, Snowflake transient tables have no Fail-safe period and a Time Travel window of zero or one day, so deleted data cannot be recovered after that. That helps privacy and hurts recovery.

To fill the table, you need a signal when a contact is created or a key field changes. Salesforce Platform Events or Change Data Capture publish record changes. HubSpot webhook subscriptions cover contact creation. A scheduled query for records created or modified since the last run is simpler and often enough. Use it unless you need enrichment within minutes.

The waterfall

A contact record passes down three providers in order of cost and accuracy until required fields are filled, and each value keeps its provider and timestamp.
Stop at the first provider that fills the gap, and keep the source with each value

A waterfall calls providers one after another and stops when the required fields are filled. Order them by a mix of cost and accuracy for your market, not alphabetically.

for each pending record:
    skip if email is on the suppression list
    required = fields still empty
    for provider in [provider_a, provider_b, provider_c]:
        result = provider.lookup(email)
        fill required fields from result, tagging provider and timestamp
        if no required fields remain: break
    mark record complete, partial or unmatched

Three properties keep it safe. Running the job twice must not create duplicates or double charges, so check the status column first. When a provider returns HTTP 429, honor the Retry-After header when present, as MDN describes, and use exponential backoff when it is absent. And store the provider name beside every value, so you can see which source to trust when two disagree.

Validate before you write back

Treat provider output as untrusted.

CheckExample
FormatValid country code, plausible employee count, LinkedIn URL in the expected pattern
ConflictThe enriched company domain matches the email domain
FreshnessThe value is newer than what the CRM holds
Personal domainsNever set a company name from a Gmail address

Rows that fail go to a review table, not the CRM. Persist the validated result in a permanent warehouse table for analytics, and keep personal fields only as long as your retention rule allows.

Write back in batches to stay inside API limits. In HubSpot, the batch upsert endpoint identifies contacts by email or a custom unique property. In Salesforce, update by record ID instead of looking up by email, so you never touch the wrong record. Write only the fields you decided to enrich, and stamp each record with an enrichment date so you can find stale ones.

What to watch each week

CheckWhy it matters
Match rate by providerShows which provider earns its place in the waterfall
Share of fields filledTells you whether the waterfall closes gaps
Failures and retriesCatches expired keys and schema changes early
Rows in reviewA growing pile means a rule or a provider has drifted
Email bounce rate on enriched contactsA direct test of accuracy; see the email list hygiene system

No dashboard replaces a manual sample. Each quarter, check 50 records by hand against LinkedIn or the company website. Refresh each field at its own pace, because titles and employers change faster than industry or country. A high no-match rate points to bad source emails, so fix capture forms before you buy another provider. The enriched fields also feed intent signals beyond firmographics, and a missing opt-out in any downstream copy is the failure to guard against first.

Related Content