# Contact Data Provenance Audit — GDPR Defensibility (`apricot_blackberry/enrichment-provenance-audit`) Actor

Audit any enriched contact list (Clay, Apollo, ZoomInfo, Lusha…): per contact and field, is the value verifiable against a public source (with source + timestamp), stale, unverifiable or broker-only? Evidence for legitimate-interest assessments and DPIAs.

- **URL**: https://apify.com/apricot\_blackberry/enrichment-provenance-audit.md
- **Developed by:** [Creator Fusion](https://apify.com/apricot_blackberry) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 audited contacts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Contact Data Provenance Audit — GDPR Defensibility for Enriched Lists

### What does it do?

Give it any enriched contact list — a Clay table, an Apollo or ZoomInfo export, a Lusha pull, a CRM segment — and it checks every row against **free public sources** (Gravatar, GitHub, Keybase, the company's own website and team pages, web search, Hacker News, DNS/RDAP). For each field you supplied it returns one of:

| Verdict | Meaning |
|---|---|
| `verified-public` | matches a public source — with the source name and check timestamp |
| `public-differs` | a public source shows a different value (likely stale) |
| `not-publicly-verifiable` | no public source holds this attribute for this person |
| `broker-only` | attribute class that is essentially never public (mobile, personal email, DOB) — provenance must come from your vendor contract or a consent record |
| `blank` | you had no value |

Plus, per contact: `_publicFootprint` (does this person exist publicly under this email at all), `_defensibilityScore` (% of supplied fields corroborated), and a plain-English `_recommendation`. The run's `SUMMARY` gives list-level percentages you can paste into a DPIA or legitimate-interest assessment.

### Why now

In July 2026 Italy's regulator fined Lusha €2M and ruled that legitimate interest does not cover selling contact data collected without a public-source basis, ordering erasure. Buyers of enriched data now need to show, per record, where the data could lawfully have come from. Enrichment vendors do not expose their sources. This Actor gives you the public-source view independently.

### How to use it

1. Paste rows as a JSON array into **Contacts** (or point at an Apify dataset). Any column names — email, name, title, company, LinkedIn, location, phone are auto-detected; use **Column overrides** for unusual headers.
2. Run. Each row comes back with your original columns plus the `_audit` verdicts and `_publicRecord`.
3. Filter `_recommendation` for `stale-or-mismatched` (refresh before use) and `no-public-footprint` (keep vendor provenance on file); export `_verifiedFields` counts and `SUMMARY` for your compliance file.

### How much does it cost?

| Event | Price |
|---|---|
| `audited-contact` — every row audited | **$0.004** ($4 per 1,000 contacts) |
| Actor start | $0.00005 |

Rows are charged whether or not a public footprint is found, because the "not verifiable" verdict is the compliance-relevant answer.

### Sample output (one row)

```json
{ "email": "torvalds@linux-foundation.org", "full_name": "Linus Torvalds", "company": "Linux Foundation", "location": "Portland, OR",
  "_publicFootprint": true, "_footprintConfidence": 75, "_defensibilityScore": 67,
  "_recommendation": "defensible: public-source provenance available",
  "_verifiedFields": ["company", "location"], "_mismatchedFields": [], "_unverifiableFields": ["full_name"], "_brokerOnlyFields": [],
  "_audit": { "company": { "verdict": "verified-public", "publicValue": "Linux Foundation", "source": "website", "checkedAt": "2026-09-25T04:55:00Z" },
              "location": { "verdict": "verified-public", "publicValue": "Portland, OR", "source": "gravatar", "checkedAt": "2026-09-25T04:55:00Z" } } }
```

### FAQ

**Does "not-publicly-verifiable" mean the data is unlawful?** No. It means no public source corroborates it, so its lawful basis has to rest on your vendor's contract, a consent record, or another Article 6 basis — this Actor tells you which records need that paperwork.

**Does it send anything to the people on the list?** No. It only queries public endpoints.

**Can I use it to clean the list too?** Yes — `_mismatchedFields` is a stale-record detector, and `_publicRecord` carries the fresh public value.

***

Built by Creator Fusion LLC. Pair with *Reverse Email Lookup — Email to Name, LinkedIn & Company* (same engine, for enrichment) and *Email Verifier — Bulk Verification with Catch-All Detection*.

# Actor input Schema

## `contacts` (type: `array`):

Rows from your enrichment export. Any columns; an email column is required. Column names are auto-detected (name, title, company, linkedin, location, phone…).

## `datasetId` (type: `string`):

Audit rows from an existing dataset instead (for example the output of another enrichment Actor).

## `columnOverrides` (type: `object`):

Map your column name → canonical field when auto-detection misses, e.g. {"work\_mail": "email", "org": "company"}. Canonical names: email, fullName, firstName, lastName, jobTitle, headline, company, companyDomain, companyWebsite, linkedinUrl, location, twitterUrl, phone, personalEmail, dob.

## `sources` (type: `array`):

Free public sources used to corroborate each field.

## `maxContacts` (type: `integer`):

Stop after this many rows.

## `maxConcurrency` (type: `integer`):

Contacts audited in parallel.

## `githubToken` (type: `string`):

Raises GitHub API limits.

## `proxyConfiguration` (type: `object`):

Residential proxy improves web-search corroboration.

## `requestTimeoutSecs` (type: `integer`):

Timeout per public source request.

## Actor input object example

```json
{
  "contacts": [
    {
      "email": "dhh@hey.com",
      "full_name": "David Heinemeier Hansson",
      "title": "CTO",
      "company": "37signals",
      "linkedin": "https://www.linkedin.com/in/david-heinemeier-hansson-374b18221",
      "mobile": "+1 555 0100"
    },
    {
      "email": "torvalds@linux-foundation.org",
      "full_name": "Linus Torvalds",
      "title": "Fellow",
      "company": "Linux Foundation",
      "location": "Portland, OR"
    }
  ],
  "columnOverrides": {},
  "sources": [
    "dns",
    "rdap",
    "gravatar",
    "github",
    "keybase",
    "website",
    "search",
    "sitePattern",
    "hackernews"
  ],
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "requestTimeoutSecs": 15
}
```

# Actor output Schema

## `audited` (type: `string`):

Your rows plus \_publicFootprint, \_defensibilityScore, per-field verdicts (\_audit) and the public record found (\_publicRecord).

## `summary` (type: `string`):

% contacts with public footprint, % fields verified / mismatched, column mapping.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "contacts": [
        {
            "email": "dhh@hey.com",
            "full_name": "David Heinemeier Hansson",
            "title": "CTO",
            "company": "37signals",
            "linkedin": "https://www.linkedin.com/in/david-heinemeier-hansson-374b18221",
            "mobile": "+1 555 0100"
        },
        {
            "email": "torvalds@linux-foundation.org",
            "full_name": "Linus Torvalds",
            "title": "Fellow",
            "company": "Linux Foundation",
            "location": "Portland, OR"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("apricot_blackberry/enrichment-provenance-audit").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "contacts": [
        {
            "email": "dhh@hey.com",
            "full_name": "David Heinemeier Hansson",
            "title": "CTO",
            "company": "37signals",
            "linkedin": "https://www.linkedin.com/in/david-heinemeier-hansson-374b18221",
            "mobile": "+1 555 0100",
        },
        {
            "email": "torvalds@linux-foundation.org",
            "full_name": "Linus Torvalds",
            "title": "Fellow",
            "company": "Linux Foundation",
            "location": "Portland, OR",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("apricot_blackberry/enrichment-provenance-audit").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "contacts": [
    {
      "email": "dhh@hey.com",
      "full_name": "David Heinemeier Hansson",
      "title": "CTO",
      "company": "37signals",
      "linkedin": "https://www.linkedin.com/in/david-heinemeier-hansson-374b18221",
      "mobile": "+1 555 0100"
    },
    {
      "email": "torvalds@linux-foundation.org",
      "full_name": "Linus Torvalds",
      "title": "Fellow",
      "company": "Linux Foundation",
      "location": "Portland, OR"
    }
  ]
}' |
apify call apricot_blackberry/enrichment-provenance-audit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,apricot_blackberry/enrichment-provenance-audit"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/dQzOSvXouzep68hjd/builds/cLTxBf6yguZ4lUVEZ/openapi.json
