# TCPA Consent Certificate API - Lead Verification Data (`nabeelbaghoor/lead-consent-certificate-api`) Actor

Check the consent certificate behind a lead before you call it. Confirms the lead's email and phone match the certificate, reads the consent language, scans the page text, and returns session metadata: page URL, IP geolocation, browser, OS, time on page and bot detection.

- **URL**: https://apify.com/nabeelbaghoor/lead-consent-certificate-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Business, Developer tools, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$12.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## TCPA Consent Certificate API - Lead Verification Data

Check the consent certificate behind a lead before you call it. One flat row per certificate, carrying the proof, the contradictions and the session behind both.

### What this actor does

- **Answers the question you actually have about a lead: may I call this person.** The provider computes one overall outcome across every check it ran, and its own guidance is to base the purchase decision on that value. It is the first column in the dataset, with the reason next to it.
- **Catches the lead whose details do not match its own certificate.** Lead matching tests the email and the phone number the lead arrived with against the ones recorded during the session. A mismatch means the certificate is real but belongs to somebody else, which is the single most useful signal a certificate carries and the one a spot check never finds.
- **Reads the consent language, not just its presence.** Verification checks whether the wording was approved, whether it named your advertiser in a one-to-one manner, whether the opt-in was a real tick rather than a pre-selected box, and whether the text was large enough and contrasted enough to be read at all.
- **Scans the page for what must and must not be there.** Give it the disclosure you require and the incentive words you forbid, and each certificate reports which terms were found and which were missing, case and whitespace insensitive.
- **Returns the session, not a verdict alone.** Page URL, parent page when framed, domain, approximate location from the IP, browser, operating system, time on page, keystrokes per minute and the provider's own bot determination all come back as ordinary columns you can sort and filter.
- **Never reads "we did not check" as "it failed".** An operation that was not asked for writes null rather than false. That distinction matters when the column is going to be evidence.
- **Treats an expired certificate as an answer, not an error.** Certificates age out, and a list of a thousand will contain some that have. Those become rows carrying the reason, and the run keeps going.
- **Keeps the one operation that writes behind an explicit switch.** Retaining a certificate stores it against your account at the provider's price. It is off unless you ask for it, and the run says so in the log when you do.
- **Charges nothing for a miss.** Only certificates the provider actually answered for are billed.

### Input

| Field | What it does |
| --- | --- |
| `certificates` | Certificate URLs or ids, one per line. A URL is reduced to its id for you. |
| `leads` | One object per lead: certificate plus email, phone, reference, vendor, campaignId. |
| `operations` | Insights, match lead, verify, retain. |
| `apiKey` | Your own API key. |
| `insightsProperties` | Which session data points to ask for. Empty means all. |
| `scanRequired` / `scanForbidden` | Text that must and must not appear on the page. |
| `scanDelimiter` | Delimiter wrapping wildcards inside a scan term. |
| `advertiserName` | Legal entity name for the one-to-one consent check. |
| `minFontSizePx` / `minContrastRatio` | Readability thresholds for the consent language. |
| `optInTypesAllowed` | Manual, pre-selected, non-interactive. |
| `matchEmail` / `matchPhone` | Contact details to test against every certificate in the plain list. |
| `retainVendor` / `retainReference` / `retainCampaignId` | Recorded on your copy when retaining. |
| `onlySuccessfulOutcome` | Keep only certificates whose overall outcome succeeded. |
| `onlyMatchedLeads` | Keep only leads whose contact details matched. |
| `onlyApprovedConsent` | Keep only certificates that passed the consent checks. |
| `excludeBotDetected` | Drop traffic flagged as non-human. |
| `minSecondsOnPage` | Drop sessions shorter than this. |
| `skipNotFound` | Leave misses out of the dataset. |
| `maxResults` | Row ceiling, and therefore the cost ceiling. |
| `requestsPerMinute` | Pacing. |

### Example output

```json
{
  "certificateId": "eb9fc4dd9bed9ad451a5648946cf4bf09b5bb947",
  "certificateUrl": "https://cert.trustedform.com/eb9fc4dd9bed9ad451a5648946cf4bf09b5bb947",
  "found": true,
  "outcome": "success",
  "outcomeReason": null,
  "matchLeadRequested": true,
  "matchLeadRan": true,
  "matchLeadSuccess": true,
  "emailMatch": true,
  "phoneMatch": false,
  "verifyRequested": true,
  "verifySuccess": true,
  "languageApproved": true,
  "oneToOneConsent": true,
  "optInTypesSatisfied": true,
  "consentLanguages": ["By clicking on the 'Get Rates' button below, I consent to be contacted"],
  "advertiserName": "Acme Inc.",
  "pageUrl": "https://www.example.com/contact.html",
  "domain": "example.com",
  "createdAt": "2024-09-18T17:07:09Z",
  "expiresAt": "2024-12-20T17:07:09Z",
  "secondsOnPage": 1129,
  "botDetected": false,
  "isFramed": false,
  "isMasked": false,
  "formInputMethod": ["typing"],
  "formInputKpm": 234.93,
  "ip": "192.168.0.1",
  "geoCity": "Dallas",
  "geoState": "Texas",
  "geoCountryCode": "US",
  "geoTimeZone": "America/Chicago",
  "browserName": "Chrome",
  "browserVersion": "128.0.0.0",
  "osName": "Mac OS X",
  "osIsMobile": false,
  "scanSuccess": false,
  "requiredTermsFound": [],
  "requiredTermsMissing": ["offer"]
}
```

### FAQ

#### What is a consent certificate?

A consent certificate is a record of the web session in which somebody submitted a form. It captures the page they were on, the wording they were shown, how they filled the form in, roughly where they were and what they were using, and it is addressed by its own URL. When a lead is bought or sold, that URL travels with it as the evidence that consent was given.

#### Why does the certificate need checking at all if the lead came with one?

Because a certificate proves that a session happened, not that it was this person's session. The certificate can be perfectly valid and still be attached to a different lead, which is why lead matching exists: it tests the email address and phone number on the lead against the ones recorded during the session. A certificate that does not match the lead it arrived with is the clearest sign available that the lead should not be contacted.

#### What is one-to-one consent?

One-to-one consent means the consumer agreed to be contacted by one named business rather than by an unspecified list of partners. The verify operation compares the consent wording on the page against the advertiser legal name you supply, normalising case, spacing and punctuation before it compares. Without an advertiser name the check cannot run and the column comes back empty rather than false.

#### What does the bot detection actually look at?

The provider derives it from the recorded session events using its own algorithms, and reports a single determination of whether those events were likely produced by a non-human. This actor returns it alongside the raw signals that inform the same judgement: seconds on page, keystrokes per minute, words per minute and how the fields were filled in, so you can set your own threshold instead of relying only on the flag.

#### What happens to certificates that have expired?

They come back as rows with the reason and are not charged for. Certificates have an expiry date, and any real list will contain some that have passed it. Sandboxed certificates, malformed ids and operations your account is not contracted for behave the same way: the run records them and moves on rather than stopping.

#### Does this actor cost anything at the provider?

Yes. This is a bring-your-own-key actor, so calls are made against your own account and billed by the provider on their own terms. Only the data points actually returned are charged for on their side. On this side you are charged per row saved, and never for a miss.

#### Which operation changes something?

Retain, and only retain. It stores the certificate against your account so it stays available after it would otherwise have expired, and that is a purchase. The other three operations read. Retain is off by default and the run logs a warning when it is switched on.

#### Can I run this without an API key?

No, and the actor will tell you so rather than failing. Every call is made with your own account's key, which you paste into the API key field or set as a secret. The key is never written to the dataset or shared between runs.

### Keyword map

TCPA consent certificate API, lead consent verification, one-to-one consent check, lead certificate insights API, consent language verification, lead matching API, TCPA compliance data, certificate claim API, lead authenticity check, bot detection on lead forms, session replay metadata API, page scan consent proof, lead buyer verification, consent proof automation.

# Actor input Schema

## `certificates` (type: `array`):

The certificate URLs or bare certificate ids to check, one per line. A full URL is reduced to its id for you, so you can paste the value exactly as it arrived on the lead.

## `leads` (type: `array`):

For lead matching: one object per lead, each with a certificate and the contact details the lead arrived with. Recognised keys are certificate, email, phone, reference, vendor and campaignId. A certificate listed here is used in preference to the same certificate in the plain list, because it carries more to work with.

## `operations` (type: `array`):

Several operations travel in one call and each reports separately. Lead matching needs an email or a phone number per certificate. Retain is the only one that writes: it stores the certificate against your own account at the provider's price, so it is off unless you ask for it.

## `apiKey` (type: `string`):

Your own API key for this provider. The actor sends it as the password of an HTTP Basic pair whose username is the fixed string API, which is what this provider expects. Bring your own key: nothing is shared between runs and the key is never written to the dataset.

## `insightsProperties` (type: `array`):

In insights mode, the individual data points to ask for. Leave empty for all of them. Only data points your account is contracted for are available, and the provider charges only for the ones it actually returns, so asking widely costs nothing extra for points it cannot answer.

## `scanRequired` (type: `array`):

Terms the recorded page content must contain, one per line. The search is case and whitespace insensitive. Use it to prove the disclosure you require was actually on screen.

## `scanForbidden` (type: `array`):

Terms the recorded page content must not contain, one per line. Use it to catch incentive wording such as free or prize that would undermine the consent.

## `scanDelimiter` (type: `string`):

The delimiter used to wrap wildcards inside a scan term. It has to be the same at both ends, for example a single pipe, a double equals or a triple pipe. Leave empty when your terms carry no wildcards.

## `advertiserName` (type: `string`):

In verify mode, the legal entity name to test the one-to-one consent wording against. Without it the one-to-one result comes back empty. The provider normalises case, extra spaces and punctuation before comparing.

## `minFontSizePx` (type: `integer`):

In verify mode, the smallest font size the consent language may be set in and still pass. Leave empty to use whatever is configured on your account.

## `minContrastRatio` (type: `integer`):

In verify mode, the smallest contrast ratio between the consent text and its background that still passes. Leave empty to use whatever is configured on your account.

## `optInTypesAllowed` (type: `array`):

In verify mode, which ways of giving consent count as valid. Leave empty to use whatever is configured on your account.

## `matchEmail` (type: `string`):

In lead matching, an email address to test against every certificate in the plain list. Per-lead addresses in the lead list always win over this. You may pass a SHA1 hash instead of the address itself.

## `matchPhone` (type: `string`):

In lead matching, a phone number to test against every certificate in the plain list. Per-lead numbers in the lead list always win over this. You may pass a SHA1 hash instead of the number itself.

## `retainVendor` (type: `string`):

In retain mode, the name of the company that supplied the lead. Stored on your copy of the certificate and available to group by in the provider's reporting. Other accounts cannot see it.

## `retainReference` (type: `string`):

In retain mode, any text that helps you find the lead again later, such as your own lead id or a link to it in another system.

## `retainCampaignId` (type: `string`):

In retain mode, the campaign that produced the lead. Recorded separately from the vendor so you can report on either.

## `onlySuccessfulOutcome` (type: `boolean`):

The provider's own guidance is to decide whether to buy a lead on the overall outcome. Turn this on to drop everything else before it reaches the dataset.

## `onlyMatchedLeads` (type: `boolean`):

Drops certificates where the lead's own email or phone number was not found on the certificate. Only meaningful when lead matching is one of the operations.

## `onlyApprovedConsent` (type: `boolean`):

Drops certificates where the consent language, one-to-one wording, opt-in type, font size or contrast failed. Only meaningful when verify is one of the operations.

## `excludeBotDetected` (type: `boolean`):

Drops rows the provider's own detection flagged as unlikely to be a human filling the form.

## `minSecondsOnPage` (type: `integer`):

Drops certificates where the consumer spent less than this long on the form. A very short session is a common signal of an unattended submission.

## `skipNotFound` (type: `boolean`):

Certificates that had expired, could not be found or were refused are written as rows with the reason, so you can see what happened to every input. Turn this on to keep only the ones that answered. Misses are never charged for either way.

## `maxResults` (type: `integer`):

Stop after this many rows. Rows are charged individually, so this is the ceiling on what a run can cost.

## `requestsPerMinute` (type: `integer`):

How fast to call the provider. Lower it if your account is rate limited.

## Actor input object example

```json
{
  "certificates": [],
  "leads": [],
  "operations": [
    "insights"
  ],
  "insightsProperties": [],
  "scanRequired": [],
  "scanForbidden": [],
  "optInTypesAllowed": [],
  "onlySuccessfulOutcome": false,
  "onlyMatchedLeads": false,
  "onlyApprovedConsent": false,
  "excludeBotDetected": false,
  "skipNotFound": false,
  "maxResults": 100,
  "requestsPerMinute": 60
}
```

# Actor output Schema

## `results` (type: `string`):

One row per certificate, carrying the overall outcome, the lead match result, the consent verification result, the page scan result and the session metadata behind them.

# 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 = {
    "certificates": [],
    "leads": [],
    "scanRequired": [],
    "scanForbidden": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/lead-consent-certificate-api").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 = {
    "certificates": [],
    "leads": [],
    "scanRequired": [],
    "scanForbidden": [],
}

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/lead-consent-certificate-api").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 '{
  "certificates": [],
  "leads": [],
  "scanRequired": [],
  "scanForbidden": []
}' |
apify call nabeelbaghoor/lead-consent-certificate-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nabeelbaghoor/lead-consent-certificate-api"
        }
    }
}

```

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/np9WBmLKAp13VoFdP/builds/9Au0lPYm2z5QrvEOE/openapi.json
