# HubSpot and Salesforce Field Mapping Tool (`zinin/us-brand-signal-crm-field-mapper`) Actor

Turn buyer-supplied signal rows into one deterministic CRM mapping template for review. Compare versioned HubSpot or Salesforce field proposals, conflicts, omissions, and evidence without logging in to or writing to either CRM.

- **URL**: https://apify.com/zinin/us-brand-signal-crm-field-mapper.md
- **Developed by:** [Tim Zinin](https://apify.com/zinin) (community)
- **Categories:** MCP servers, Automation, Marketing
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.50 / 1,000 delivered crm mapping reports

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/platform/actors/running/actors-in-store#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

## US Brand Signal CRM Field Mapper

Convert buyer-supplied signal rows into one deterministic, review-required CRM mapping template without logging in to HubSpot or Salesforce and without claiming account-schema compatibility.

**Built for:** marketing operations, CRM migration, RevOps, and data-governance teams preparing a mapping proposal before an account-specific schema review.

**Commercial result unit:** one complete Dataset report. Input rows are bounded work units, not separate billable results. A normal paid run also has the separate Apify Actor start event.

![CRM Field Mapping Templates: buyer input to evidence-backed action](https://raw.githubusercontent.com/TimmyZinin/apify-actor-assets/d035f37d6d45de4a0abcdc4ce6e212a29605dfa0/commercial115/us-brand-signal-crm-field-mapper/readme-hero.webp)

### Turn a bounded inline dataset into review-ready evidence

Most operational mistakes around compact analytics do not begin with arithmetic. They begin when a spreadsheet loses the input definition, a dashboard hides a denominator, a mapping template is mistaken for a verified destination schema, or a repeated run is assumed to be free and idempotent. US Brand Signal CRM Field Mapper is designed to keep those boundaries visible.

You submit one closed mapping of one to nine fields plus one to 100 buyer-owned signal rows with stable identities, buyer-asserted timestamps, and recorded HTTPS evidence links. The Actor validates a closed JSON contract, canonicalizes the observations, performs deterministic calculations, writes one report, and records the current-run delivery state in KVS `OUTPUT`. There is no browser automation, source scraping, CRM login, identity lookup, LLM inference, or external enrichment. The buyer remains responsible for the rights, meaning, quality, and retention of submitted data.

#### What the Actor gives you

- One deterministic Dataset report rather than one paid row per input observation.
- Stable digests that bind normalized inputs and calculated output tables.
- An additive decision block with stable identity, evidence scope, confidence basis, gaps, human action, and `safeToAutomate:false`.
- A current-run KVS `OUTPUT` envelope with requested, unique, duplicate, successful, failed, delivered, paid, free, withheld, anomalous, ambiguous, partial, budget, fatal, and replay fields.
- Exact named-event delivery accounting: a successful report requires the `result-found` counter to move from zero to one.
- An explicit no-external-verification boundary so a formatted result cannot be mistaken for source truth.

#### What it does not give you

It does not prove demand, identity, causality, attribution, consent, ownership, completeness, statistical significance, destination-system compatibility, or a business outcome. It does not contact people, modify a CRM, enrich an entity, fetch a recorded URL, or choose an authoritative source. The recommended action is `VERIFY_TARGET_ACCOUNT_SCHEMA_AND_REVIEW_EVERY_PROPOSAL_BEFORE_IMPORT`, and the report remains unsafe for autonomous consequential use.

### Good-fit workflows

- Prepare a review artifact before a CRM administrator creates or approves custom signal properties.
- Compare an incoming evidence contract with a versioned HubSpot or Salesforce template without writing to either platform.
- Expose unmapped target fields and destructive transform risks before an import or automation is configured.
- Preserve a deterministic mapping digest so a later account-schema review can identify exactly what changed.

This product is strongest as one small, auditable step inside a larger workflow. It can replace repetitive counting or formatting, preserve evidence and limitations, and produce a consistent review object. It cannot replace the qualified human who understands why the dataset exists, how it was collected, and what decision standard applies.

![CRM Field Mapping Templates: evidence-to-action workflow](https://raw.githubusercontent.com/TimmyZinin/apify-actor-assets/d035f37d6d45de4a0abcdc4ce6e212a29605dfa0/commercial115/us-brand-signal-crm-field-mapper/readme-workflow.webp)

### Evidence-to-action workflow

1. Define the review question and confirm that you have the right to process every submitted field.
2. Remove personal data, credentials, signed URLs, confidential notes, and unnecessary free text before submission.
3. Submit the closed JSON input through Apify Console, API, a Task, or an authorized agent workflow.
4. The Actor validates types, closed enums, maximum sizes, stable identifiers, timestamps, and HTTPS URL syntax where applicable.
5. The Actor canonicalizes declared work units and rejects duplicate stable IDs within every uniqueness scope; it does not silently collapse buyer rows.
6. The Actor validates the closed target profile, checks field/transform compatibility, groups rows by entity, applies only declared transforms, reports missing target fields, and produces a deterministic proposal. New v1.1 profiles use HubSpot company `name` and Salesforce Account `Name`; custom properties remain organization-specific and unverified.
7. The report builder validates the complete output shape, decision evidence, digests, counts, and size boundary before delivery.
8. One linked Dataset push requests the named `result-found` event. The runtime verifies the exact counter transition.
9. KVS `OUTPUT` records terminal status, current run binding, delivery facts, work-unit reconciliation, failure stage, and replay truth.
10. A human reads the report, its gaps, and the original authorized source context before any downstream action.

### How the result is calculated

The Actor validates the closed target profile, checks field/transform compatibility, groups rows by entity, applies only declared transforms, reports missing target fields, and produces a deterministic proposal. New v1.1 profiles use HubSpot company `name` and Salesforce Account `Name`; custom properties remain organization-specific and unverified.

The calculation is intentionally deterministic. Equivalent normalized input produces the same report content and digests. Object-key order or superficial JSON formatting does not change the business result. Closed enums stop undocumented categories from leaking into downstream tables. Integer counts and basis points avoid hidden floating-point presentation drift where the contract uses them.

Determinism is not verification. A perfectly repeatable calculation can still describe biased, stale, unlawfully collected, or misunderstood input. That is why the report keeps three concepts separate:

| Layer | Question answered | Boundary |
|---|---|---|
| Calculated signal | What did the Actor derive from the submitted rows? | Describes only normalized buyer input. |
| Confidence | How complete and interpretable is the evidence available to this Actor? | Does not measure truth or business value. |
| Gaps | What is missing before a consequential use? | Missing evidence is not safely assumed to be zero. |
| Recommended action | What should a reviewer inspect next? | It is a routing instruction, not permission to automate. |
| Billing | Was this one report delivered and reconciled in the current run? | It does not certify the business content. |

### Input

The public example is a real bounded contract fixture used by tests. Start with it, then replace values with authorized data while preserving field types and closed enums.

```json
{
  "schemaVersion": "1.1",
  "target": "hubspot_v1_1",
  "mapping": [
    {"sourceField": "brand_name", "targetField": "name", "transform": "trim"},
    {"sourceField": "signal_status", "targetField": "signal_status", "transform": "lowercase"},
    {"sourceField": "score", "targetField": "signal_score", "transform": "number_decimal_2"},
    {"sourceField": "observed_at", "targetField": "last_observed_at", "transform": "date_yyyy_mm_dd"}
  ],
  "rows": [
    {"signalId": "sig-001", "entityId": "brand-acme", "sourceEvidenceId": "ev-001", "sourceUrl": "https://evidence.example.com/acme/offer", "observedAt": "2026-08-05T10:00:00Z", "values": {"brand_name": " Acme ", "signal_status": "ACTIVE", "score": 82.5}},
    {"signalId": "sig-002", "entityId": "brand-acme", "sourceEvidenceId": "ev-002", "sourceUrl": "https://evidence.example.com/acme/news", "observedAt": "2026-08-05T11:00:00Z", "values": {"brand_name": "Acme", "signal_status": "ACTIVE", "score": 82.5}}
  ]
}
```

#### Input governance

- Submit only data your organization is authorized to process for the stated purpose.
- Prefer opaque identifiers that cannot be reversed outside your controlled system.
- Do not submit names, emails, phone numbers, device IDs, cookies, advertising IDs, credentials, access tokens, signed URLs, or secrets.
- A hash of a stable personal identifier may still be personal data. Hashing alone is not anonymization.
- Recorded HTTPS URLs are stored as buyer input. They are not fetched, resolved, malware-scanned, authenticated, or verified.
- Use Apify storage retention and access controls appropriate to your organization. Delete runs and datasets when the review purpose ends.
- Keep the original collection notice, lawful basis, consent or contract, provenance, and deletion obligations outside this report.

The Input schema is the public first gate, but runtime validation remains normative. Do not rely on JavaScript coercion or unknown properties. If your producer changes its contract, version and test that change rather than hoping the Actor interprets it.

#### Report version migration

Input **schemaVersion 1.0** selects the exact legacy report contract. It preserves the previously published fields and legacy resultDigest calculation and deliberately has no decision property. The earliest Metrics Aggregator v1.0 output did not echo a schemaVersion field, so that omission is also preserved exactly. Input **schemaVersion 1.1** selects the explicit enhanced contract and is the public default: it adds the decision evidence layer, and its resultDigest binds that enhanced report body. Never compare digests across versions as if they represented the same bytes, never inject a decision object into a v1.0 report, and never accept a v1.1 report without one. Pin the input version and validate the corresponding output shape.

### Output

One successful run writes one report shaped as `us_brand_signal_crm_field_mapper`. The complete executable fixture is shown below so an integration can be developed against the same structure used by contract tests.

<details>
<summary>Complete example Dataset report</summary>

```json
{
  "schemaVersion": "1.1",
  "reportType": "us_brand_signal_crm_field_mapper",
  "attribution": {
    "type": "buyer_supplied_normalized_signals",
    "meaning": "The buyer supplied normalized rows, entity IDs, evidence IDs, and URLs. Proposals are unsent field/value suggestions only; this Actor does not infer identity, validate external facts, or call a CRM."
  },
  "target": "hubspot_v1_1",
  "mapping": [
    {
      "sourceField": "observed_at",
      "targetField": "last_observed_at",
      "transform": "date_yyyy_mm_dd"
    },
    {
      "sourceField": "brand_name",
      "targetField": "name",
      "transform": "trim"
    },
    {
      "sourceField": "score",
      "targetField": "signal_score",
      "transform": "number_decimal_2"
    },
    {
      "sourceField": "signal_status",
      "targetField": "signal_status",
      "transform": "lowercase"
    }
  ],
  "unmappedTargetFields": [
    "external_id",
    "notes",
    "signal_type",
    "source_evidence_id",
    "source_url"
  ],
  "totalRows": 2,
  "totalEntities": 1,
  "entities": [
    {
      "entityId": "brand-acme",
      "rowCount": 2,
      "proposals": [
        {
          "targetField": "last_observed_at",
          "sourceField": "observed_at",
          "transform": "date_yyyy_mm_dd",
          "value": "2026-08-05",
          "signalIds": [
            "sig-001",
            "sig-002"
          ],
          "sourceEvidenceIds": [
            "ev-001",
            "ev-002"
          ],
          "sourceUrls": [
            "https://evidence.example.com/acme/news",
            "https://evidence.example.com/acme/offer"
          ]
        },
        {
          "targetField": "name",
          "sourceField": "brand_name",
          "transform": "trim",
          "value": "Acme",
          "signalIds": [
            "sig-001",
            "sig-002"
          ],
          "sourceEvidenceIds": [
            "ev-001",
            "ev-002"
          ],
          "sourceUrls": [
            "https://evidence.example.com/acme/news",
            "https://evidence.example.com/acme/offer"
          ]
        },
        {
          "targetField": "signal_score",
          "sourceField": "score",
          "transform": "number_decimal_2",
          "value": 82.5,
          "signalIds": [
            "sig-001",
            "sig-002"
          ],
          "sourceEvidenceIds": [
            "ev-001",
            "ev-002"
          ],
          "sourceUrls": [
            "https://evidence.example.com/acme/news",
            "https://evidence.example.com/acme/offer"
          ]
        },
        {
          "targetField": "signal_status",
          "sourceField": "signal_status",
          "transform": "lowercase",
          "value": "active",
          "signalIds": [
            "sig-001",
            "sig-002"
          ],
          "sourceEvidenceIds": [
            "ev-001",
            "ev-002"
          ],
          "sourceUrls": [
            "https://evidence.example.com/acme/news",
            "https://evidence.example.com/acme/offer"
          ]
        }
      ],
      "conflicts": [],
      "omissions": []
    }
  ],
  "counts": {
    "rows": 2,
    "entities": 1,
    "proposals": 4,
    "conflicts": 0,
    "omissions": 0
  },
  "inputDigest": "f28f2005824071fd15c8b894d8aebd93e5870da9cf69d1e5bf893e0086c3b9c9",
  "mappingDigest": "dba0114b7155c04200632f07cc49f8a09639f4d0e2c886cfd62865aa590d96a6",
  "entitiesDigest": "f05ba4615031b0a788070ad010d83c4e913428d4d78f31d236456d93abf9e4b2",
  "countsDigest": "1b43c771c2466701f3f1c491f4d8c99df7f56c2a84e7867a67d7ddd21bec5f1e",
  "decision": {
    "decisionSchemaVersion": "1.0",
    "recordType": "us_brand_signal_crm_field_mapper",
    "entityId": "us_brand_signal_crm_field_mapper:f28f2005824071fd",
    "observedAt": null,
    "freshness": {
      "status": "unknown",
      "asOf": null,
      "basis": "buyer_supplied_snapshot_no_source_fetch"
    },
    "change": {
      "status": "not_measured",
      "previousEntityId": null,
      "basis": "stateless_single_run"
    },
    "confidence": {
      "score": 50,
      "band": "medium",
      "basis": "deterministic_computation_over_unverified_buyer_input",
      "gaps": [
        "target_account_schema_not_verified",
        "custom_fields_are_account_specific",
        "buyer_input_not_externally_verified",
        "no_crm_write_performed"
      ]
    },
    "evidence": {
      "inputDigest": "f28f2005824071fd15c8b894d8aebd93e5870da9cf69d1e5bf893e0086c3b9c9",
      "source": "buyer_supplied_inline",
      "externalVerification": false,
      "target": "hubspot_v1_1",
      "targetSchemaVerified": false,
      "entityCount": 1,
      "proposalCount": 4,
      "conflictCount": 0
    },
    "recommendedAction": "VERIFY_TARGET_ACCOUNT_SCHEMA_AND_REVIEW_EVERY_PROPOSAL_BEFORE_IMPORT",
    "priority": "review",
    "safeToAutomate": false,
    "summary": "Prepared 4 unverified mapping proposals for 1 entities; no HubSpot or Salesforce account schema was queried and no CRM write occurred.",
    "failureDiagnostics": null,
    "billing": {
      "billable": true,
      "eventName": "result-found",
      "unit": "one_delivered_crm_mapping_report"
    }
  },
  "resultDigest": "b4dc2668d9f6266bb4bf7b2025eb818117527d7e3736549d8417696b349b24ac"
}
```

</details>

#### Important report fields

| Field | Meaning |
|---|---|
| `schemaVersion` | Backward-compatibility boundary for the report. |
| `reportType` | Stable discriminator for this Actor family. |
| `inputDigest` | SHA-256 binding for canonical validated input. |
| `resultDigest` | SHA-256 binding for the validated report body of that exact schema version; v1.0 and v1.1 intentionally differ. |
| `attribution` | States that observations are buyer supplied and not externally verified. |
| `decision.entityId` | Stable report-level identity derived from the canonical business input. |
| `decision.freshness` | Unknown because the Actor does not observe an external source. |
| `decision.change` | Not measured; one run is a deterministic snapshot of submitted rows. |
| `decision.confidence` | Evidence sufficiency basis and explicit gaps, not a truth score. |
| `decision.evidence` | Input digest and source boundary used by the review object. |
| `decision.recommendedAction` | `VERIFY_TARGET_ACCOUNT_SCHEMA_AND_REVIEW_EVERY_PROPOSAL_BEFORE_IMPORT`. |
| `decision.safeToAutomate` | Always false for the business decision. |
| `decision.failureDiagnostics` | Null on a valid report; terminal runtime failures live in KVS `OUTPUT`. |
| `decision.billing` | Declares the report as the one `result-found` unit; current-run proof remains in `OUTPUT`. |
| `target` | Selected versioned template. Prefer `hubspot_v1_1` or `salesforce_v1_1` for new work. |
| `mapping` | Validated source-field, target-field, and transform proposal. |
| `entities` | Buyer rows grouped into deterministic entity proposals with recorded source evidence. |
| `unmappedTargetFields` | Template properties that were not supplied by the declared mapping. |
| `targetSchemaVerified` | Decision evidence is always false because the Actor never queries the destination account. |
| `mappingDigest / entitiesDigest / countsDigest` | Integrity bindings for the proposal and its calculated summary. |

#### Honest evidence gaps

- The Actor does not authenticate to HubSpot or Salesforce and cannot confirm an account property exists.
- Custom HubSpot properties and Salesforce `__c` fields are organization-specific.
- Recorded HTTPS evidence URLs are syntax-validated but never fetched, resolved, or verified.
- Buyer-supplied `observedAt` values are validated timestamps, not independently verified freshness evidence.

The Dataset view exposes the decision block and the central report fields so a Store user does not need to download raw JSON to see the main limitation. Raw JSON remains the authoritative analytical record because a table view can flatten or truncate nested values for display.

### KVS OUTPUT: terminal and billing truth

Read the default Key-Value Store record named `OUTPUT` after every run. The Dataset answers “what report was delivered?”; `OUTPUT` answers “what happened in this exact run?”

Successful and failure envelopes use three terminal status classes:

| Status | Meaning | Retry guidance |
|---|---|---|
| `success` | One report validated, delivered, and reconciled with exact named-event movement. | A new run is a new commercial execution and can charge again. |
| `error` | Failure was established before a Dataset delivery attempt. | Repair non-retryable input/config errors; retry only an explicitly retryable operational failure. |
| `delivery_error` | A delivery attempt produced an unknown or contradictory post-push state. | Do not blindly retry. Reconcile Dataset, PPE counters, logs, and KVS manually. |

The `run` block separates input-row work from the single commercial report:

- `requestedCount`, `uniqueCount`, and `duplicateCount` describe input work units.
- `successfulCount` and `failedCount` describe validated processing outcomes in those same work units.
- `deliveredRowCount` and `paidRowCount` describe report delivery; a normal success is one and one.
- `freeRowCount` is one only when a report was confirmed in the Dataset but the exact named-event delta was zero (`result_uncharged`); ordinary success is paid one / free zero.
- `withheldRowCount` remains zero in this family because a pre-delivery rejection does not establish or materialize a normalized Dataset work unit.
- `anomalousChargeCount` and `ambiguousDeliveryCount` make settlement contradictions queryable.
- `partial`, `budgetStopped`, and `fatalError` describe terminal completeness.
- `replaySafe:false` on a successful paid report warns that starting a new run is not a free replay.
- `safeToAutomate:false` prevents the analytics report from being mistaken for a consequential decision.

### Pricing and unit economics

This Actor uses pay per event. The published unit is **one complete delivered report**, irrespective of how many bounded input rows were used to calculate it. The configured Actor start event is separate.

| Tier | Actor start | One delivered report | One successful run total\* |
|---|---:|---:|---:|
| FREE | $0.00500 | $0.01000 | $0.01500 |
| BRONZE | $0.00475 | $0.00950 | $0.01425 |
| SILVER | $0.00450 | $0.00900 | $0.01350 |
| GOLD | $0.00425 | $0.00850 | $0.01275 |
| PLATINUM | $0.00410 | $0.00820 | $0.01230 |
| DIAMOND | $0.00400 | $0.00800 | $0.01200 |

\*The total shown is the configured start event plus one configured report event for that tier. The live Apify pricing panel is the current source of truth. Platform credits, account agreements, taxes, storage, network, proxy, or other platform consumption can change what a particular buyer sees.

The runtime does not estimate affordability from the advertised maximum alone. It reads the current charge cap, validates exact configured prices, verifies that the already-spent amount includes the start event where required, and reserves enough remaining budget for one report. A positive infinite platform cap is treated as genuinely unlimited; malformed or contradictory pricing state fails closed.

#### Exact result-event invariant

Inside the paid delivery section the Actor reads `getChargedEventCount('result-found')` before the linked push, performs exactly one `pushData(report, 'result-found')`, and reads the named counter afterward. A confirmed success requires the named counter to move from zero to one. Aggregate receipt counts may include both the automatic start and report events, so the named counter—not a vague positive aggregate—is the decisive unit proof.

If the counter cannot be read before delivery, no report is pushed and the run is replay-safe with respect to that failed attempt. If the push or post-push counter is unknown, the Actor does not retry the push and records `delivery_error`, ambiguity, and `replaySafe:false`. This protects against duplicate delivery and duplicate charging after an uncertain network or storage outcome.

### Run with Apify API

Keep `APIFY_TOKEN` in an environment variable. Never put tokens, signed URLs, or real confidential input into source control or copied terminal output.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/zinin~us-brand-signal-crm-field-mapper/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  --data-binary @actors/us-brand-signal-crm-field-mapper/examples/input.json
```

Poll that exact run to terminal, then read its default KVS `OUTPUT` and Dataset. Do not create a second run as a polling mechanism.

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const input = {
  "schemaVersion": "1.1",
  "target": "hubspot_v1_1",
  "mapping": [
    {"sourceField": "brand_name", "targetField": "name", "transform": "trim"},
    {"sourceField": "signal_status", "targetField": "signal_status", "transform": "lowercase"},
    {"sourceField": "score", "targetField": "signal_score", "transform": "number_decimal_2"},
    {"sourceField": "observed_at", "targetField": "last_observed_at", "transform": "date_yyyy_mm_dd"}
  ],
  "rows": [
    {"signalId": "sig-001", "entityId": "brand-acme", "sourceEvidenceId": "ev-001", "sourceUrl": "https://evidence.example.com/acme/offer", "observedAt": "2026-08-05T10:00:00Z", "values": {"brand_name": " Acme ", "signal_status": "ACTIVE", "score": 82.5}},
    {"signalId": "sig-002", "entityId": "brand-acme", "sourceEvidenceId": "ev-002", "sourceUrl": "https://evidence.example.com/acme/news", "observedAt": "2026-08-05T11:00:00Z", "values": {"brand_name": "Acme", "signal_status": "ACTIVE", "score": 82.5}}
  ]
};
const run = await client.actor('zinin/us-brand-signal-crm-field-mapper').call(input);
const outputRecord = await client
  .keyValueStore(run.defaultKeyValueStoreId)
  .getRecord('OUTPUT');
const output = outputRecord?.value;

if (!output || output.runId !== run.id) {
  throw new Error('Missing or cross-run OUTPUT receipt');
}
if (output.status !== 'success') {
  throw new Error(`Actor terminal status: ${output.status}`);
}
if (output.run.paidRowCount !== 1 || output.run.replaySafe !== false) {
  throw new Error('Unexpected result-unit or replay contract');
}

const { items } = await client.dataset(run.defaultDatasetId).listItems();
if (items.length !== 1) throw new Error('Expected exactly one report');
console.log(items[0]);
```

#### Python

```python
import json
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
with open('actors/us-brand-signal-crm-field-mapper/examples/input.json', encoding='utf-8') as handle:
    actor_input = json.load(handle)

run = client.actor('zinin/us-brand-signal-crm-field-mapper').call(run_input=actor_input)
output_record = client.key_value_store(
    run['defaultKeyValueStoreId']
).get_record('OUTPUT')
output = output_record['value'] if output_record else None

if not output or output.get('runId') != run['id']:
    raise RuntimeError('Missing or cross-run OUTPUT receipt')
if output.get('status') != 'success':
    raise RuntimeError('Actor terminal status: {}'.format(output.get('status')))
if output['run']['paidRowCount'] != 1:
    raise RuntimeError('Expected one paid report')

items = list(client.dataset(run['defaultDatasetId']).iterate_items())
if len(items) != 1:
    raise RuntimeError('Expected exactly one Dataset report')
print(items[0])
```

### Tasks, schedules, agents, and MCP-style orchestration

An Apify Task can pin the exact input for a repeated internal workflow. A schedule intentionally creates a new run and therefore may create a new start charge and report charge every time. This family does not maintain cross-run idempotency state. If your workflow needs deduplication across schedules, compare `inputDigest`, `resultDigest`, business period, and your own request identity before starting another paid run.

An agent or MCP-connected automation can call the Actor, but it must treat the return value as an evidence object rather than a decision. Require the agent to:

1. Verify that `OUTPUT.runId` matches the run it started.
2. Branch on `status`, not only platform run completion.
3. Refuse an automatic retry after `delivery_error`.
4. Cite `decision.confidence.gaps` whenever it summarizes the report.
5. Preserve `inputDigest`, `resultDigest`, and the original authorized input lineage.
6. Route `VERIFY_TARGET_ACCOUNT_SCHEMA_AND_REVIEW_EVERY_PROPOSAL_BEFORE_IMPORT` to a human queue.
7. Never transform `safeToAutomate:false` into an autonomous CRM update, outreach, deletion, eligibility, or targeting decision.

### Reliability and replay model

The Actor is intentionally stateless across runs. Canonical digests make two outputs comparable, but they do not prevent a caller from buying the same calculation twice. A successful report therefore says `replaySafe:false`: rerunning is a new commercial action. This is different from a pre-push failure, which can be replay-safe because no Dataset delivery was attempted.

For robust automation:

- store the exact run ID, KVS ID, Dataset ID, input digest, result digest, and platform charged-event counts;
- poll the same run instead of starting a replacement;
- on timeout, reconcile the original run before making any retry decision;
- on `delivery_error`, stop and inspect the original Dataset and named-event count;
- do not use Dataset emptiness alone as proof that nothing was delivered;
- compare the report digest only after validating schema version and report type;
- keep cross-run business idempotency in your own system if duplicate purchases are unacceptable.

### Privacy, rights, and retention

No third-party website is scraped by this Actor, which removes a source-licensing dependency but does not remove the buyer’s obligations. You must have the right to submit the data, calculate the report, store the output, and use it for the declared purpose.

Opaque identifiers can still be personal or confidential when another system can link them back to a person. Do not assume that hashing, pseudonymization, or removing names makes the data anonymous. Avoid personal data unless your organization has an approved purpose, access model, notice, retention period, deletion process, and applicable legal basis.

Apify run input, Dataset rows, KVS records, and logs may persist according to workspace and storage settings. Minimize input before the run, restrict workspace access, avoid secrets and signed URLs, set suitable retention, and delete stored runs when no longer needed. The Actor does not perform deletion in an upstream system and does not know whether a data subject request applies to your source.

### Failure model and troubleshooting

#### Input is rejected before a report appears

Read KVS `OUTPUT` and the platform run status. Correct schema, bounds, enum, URL, timestamp, or transform errors. A closed contract rejects unknown fields on purpose; silently accepting them would make downstream interpretation unsafe.

#### Dataset is empty

An empty Dataset is not a successful zero report for this family. Inspect `OUTPUT.status`, `terminal`, and `run`. A validation, pricing, KVS, or pre-delivery failure may correctly produce no Dataset row.

#### The run says delivery\_error

Do not press rerun. Reconcile the original run ID, Dataset, KVS record, log, and `result-found` charged-event count. The original push may have crossed the delivery boundary even if its response was lost.

#### The result looks unchanged

Equivalent normalized input is expected to produce the same content digest. That does not make the new run free. Compare run IDs and billing receipts separately from report equality.

#### A calculated count is surprising

Inspect canonicalized inputs, duplicate handling, closed enum meanings, denominators, and the explicit evidence gaps. The Actor does not inspect the business process that produced the rows.

#### OUTPUT is missing

Treat the automation receipt as unavailable. Use the platform terminal state and original run evidence; do not invent success from a log fragment or assume a new run is safe. A hosted terminal failure is preferable to a false successful receipt.

### Honest limitations

- The Actor does not authenticate to HubSpot or Salesforce and cannot confirm an account property exists.
- Custom HubSpot properties and Salesforce `__c` fields are organization-specific.
- Recorded HTTPS evidence URLs are syntax-validated but never fetched, resolved, or verified.
- Buyer-supplied `observedAt` values are validated timestamps, not independently verified freshness evidence.
- No external demand, customer, revenue, accuracy, coverage, or outcome claim is created by this Actor.
- No URL is fetched, so URL syntax never proves the referenced page exists or supports the submitted statement.
- No run compares itself with historical runs; freshness and change remain unknown or not measured.
- No output is legal, financial, medical, identity, eligibility, or investment advice.
- The decision layer routes review. It does not authorize outreach, profiling, account mutation, or an irreversible action.
- The current account has limited public usage evidence; product demand should be validated separately rather than inferred from a listing.

### FAQ

**Is this a scraper?** No. The runtime processes only the bounded JSON supplied to the run and makes no source requests.

**Does it verify evidence URLs?** No. URLs are recorded where the input contract supports them, but the Actor never fetches or authenticates them.

**Why is there only one Dataset row?** The commercial product is the complete report. Input rows are calculation work units, not separately sold outputs.

**Can the same report be charged again?** Yes, if you intentionally start a new successful run. Content determinism is not cross-run payment idempotency.

**Can I rely on the decision score alone?** No. Read the basis, gaps, original authorized input lineage, and recommended human action.

**Can I send personal data?** The safest default is no. If your approved use requires it, your organization must establish purpose, minimization, access, retention, deletion, and legal compliance outside the Actor.

**Does `safeToAutomate:false` mean the Actor is useless for automation?** No. Collection, validation, counting, formatting, integrity binding, and review-queue routing can be automated. The consequential business decision remains human.

**How do I report a reproducible issue?** Provide the Actor version, run ID, sanitized input, KVS `OUTPUT` status, and whether the Dataset contains a report. Never share a token, signed storage URL, or confidential row.

**Is the output ready to import automatically?** No. `safeToAutomate` is false. A CRM administrator must verify internal property names, types, permissions, picklists, and retention rules in the destination account.

**Why are the legacy `hubspot` and `salesforce` targets still accepted?** They remain for backward compatibility. New integrations should choose a v1.1 profile; the report still labels every profile as unverified until the buyer performs account-specific review.

### Related deterministic signal tools

| Actor | Best used for |
|---|---|
| [US Brand Signal Co-occurrence Analyzer](https://apify.com/zinin/us-brand-signal-cooccurrence-analyzer) | Convert buyer-supplied opaque signal groups into one deterministic pair-analysis report with support counts, Jaccard overlap, explicit evidence gaps, and a human review action. |
| [US Brand Signal Cross-Tabulator](https://apify.com/zinin/us-brand-signal-cross-tabulator) | Convert buyer-supplied row and column dimensions into one complete deterministic matrix with zero cells, exact denominators, evidence gaps, and a human review action. |
| [US Brand Signal Merge Report](https://apify.com/zinin/us-brand-signal-merge-report) | Compare two buyer-supplied signal sets in one deterministic union report that preserves one-sided records, exact duplicates, conflicts, source context, and a human review boundary. |
| [US Brand Signal Metrics Aggregator](https://apify.com/zinin/us-brand-signal-metrics-aggregator) | Convert buyer-supplied signal rows into one deterministic metrics report with complete closed-enum counts, average confidence basis points, warnings, evidence gaps, and a human review action. |

Choose the smallest tool that answers the declared review question. Co-occurrence measures submitted overlap; Cross-Tabulator builds a submitted matrix; Metrics Aggregator summarizes closed categories; Merge Report preserves two-sided differences; CRM Field Mapper prepares an unverified destination template. None of them turns buyer-supplied rows into independently verified market truth.

### Support and change control

Integrations should pin `schemaVersion`, verify `reportType`, retain complete raw JSON, and review release notes before accepting new enum or profile versions. Additive fields can improve evidence without removing legacy fields, but a new target profile or business interpretation must remain explicit. If a downstream system ignores unknown fields, it must still fail closed on an unknown schema version.

When requesting a feature, describe the buyer, authorized input, review decision, required evidence, acceptable bounds, and failure behavior. A request to “make the score smarter” is not sufficient unless the additional source, rights, validation method, and human decision boundary are defined.

# Actor input Schema

## `schemaVersion` (type: `string`):

Use 1.1 for the additive decision layer. Legacy 1.0 preserves the exact closed report and digest contract without decision. Apify's UI schema cannot express the target dependency; runtime rejects every v1.1-only target or field when this value is 1.0.

## `target` (type: `string`):

Use a v1.1 template for new work. The Actor never queries your CRM account schema; every proposal requires review. Legacy hubspot/salesforce values remain accepted for backward compatibility, while runtime rejects hubspot\_v1\_1/salesforce\_v1\_1 with schemaVersion 1.0.

## `mapping` (type: `array`):

Each source field may appear once and must map to the exact target field allowed for the selected CRM.

## `rows` (type: `array`):

One to one hundred rows with explicit opaque entity IDs and source evidence.

## Actor input object example

```json
{
  "schemaVersion": "1.1",
  "target": "hubspot_v1_1",
  "mapping": [
    {
      "sourceField": "brand_name",
      "targetField": "name",
      "transform": "trim"
    },
    {
      "sourceField": "signal_status",
      "targetField": "signal_status",
      "transform": "lowercase"
    },
    {
      "sourceField": "score",
      "targetField": "signal_score",
      "transform": "number_decimal_2"
    },
    {
      "sourceField": "observed_at",
      "targetField": "last_observed_at",
      "transform": "date_yyyy_mm_dd"
    }
  ],
  "rows": [
    {
      "signalId": "sig-001",
      "entityId": "brand-acme",
      "sourceEvidenceId": "ev-001",
      "sourceUrl": "https://evidence.example.com/acme/offer",
      "observedAt": "2026-08-05T10:00:00Z",
      "values": {
        "brand_name": " Acme ",
        "signal_status": "ACTIVE",
        "score": 82.5
      }
    },
    {
      "signalId": "sig-002",
      "entityId": "brand-acme",
      "sourceEvidenceId": "ev-002",
      "sourceUrl": "https://evidence.example.com/acme/news",
      "observedAt": "2026-08-05T11:00:00Z",
      "values": {
        "brand_name": "Acme",
        "signal_status": "ACTIVE",
        "score": 82.5
      }
    }
  ]
}
```

# Actor output Schema

## `OUTPUT` (type: `string`):

API link to the terminal receipt after the Actor stores it.

## `report` (type: `string`):

API link to the one report row after the result event is confirmed.

# 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 = {
    "schemaVersion": "1.1",
    "target": "hubspot_v1_1",
    "mapping": [
        {
            "sourceField": "brand_name",
            "targetField": "name",
            "transform": "trim"
        },
        {
            "sourceField": "signal_status",
            "targetField": "signal_status",
            "transform": "lowercase"
        },
        {
            "sourceField": "score",
            "targetField": "signal_score",
            "transform": "number_decimal_2"
        },
        {
            "sourceField": "observed_at",
            "targetField": "last_observed_at",
            "transform": "date_yyyy_mm_dd"
        }
    ],
    "rows": [
        {
            "signalId": "sig-001",
            "entityId": "brand-acme",
            "sourceEvidenceId": "ev-001",
            "sourceUrl": "https://evidence.example.com/acme/offer",
            "observedAt": "2026-08-05T10:00:00Z",
            "values": {
                "brand_name": " Acme ",
                "signal_status": "ACTIVE",
                "score": 82.5
            }
        },
        {
            "signalId": "sig-002",
            "entityId": "brand-acme",
            "sourceEvidenceId": "ev-002",
            "sourceUrl": "https://evidence.example.com/acme/news",
            "observedAt": "2026-08-05T11:00:00Z",
            "values": {
                "brand_name": "Acme",
                "signal_status": "ACTIVE",
                "score": 82.5
            }
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("zinin/us-brand-signal-crm-field-mapper").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 = {
    "schemaVersion": "1.1",
    "target": "hubspot_v1_1",
    "mapping": [
        {
            "sourceField": "brand_name",
            "targetField": "name",
            "transform": "trim",
        },
        {
            "sourceField": "signal_status",
            "targetField": "signal_status",
            "transform": "lowercase",
        },
        {
            "sourceField": "score",
            "targetField": "signal_score",
            "transform": "number_decimal_2",
        },
        {
            "sourceField": "observed_at",
            "targetField": "last_observed_at",
            "transform": "date_yyyy_mm_dd",
        },
    ],
    "rows": [
        {
            "signalId": "sig-001",
            "entityId": "brand-acme",
            "sourceEvidenceId": "ev-001",
            "sourceUrl": "https://evidence.example.com/acme/offer",
            "observedAt": "2026-08-05T10:00:00Z",
            "values": {
                "brand_name": " Acme ",
                "signal_status": "ACTIVE",
                "score": 82.5,
            },
        },
        {
            "signalId": "sig-002",
            "entityId": "brand-acme",
            "sourceEvidenceId": "ev-002",
            "sourceUrl": "https://evidence.example.com/acme/news",
            "observedAt": "2026-08-05T11:00:00Z",
            "values": {
                "brand_name": "Acme",
                "signal_status": "ACTIVE",
                "score": 82.5,
            },
        },
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("zinin/us-brand-signal-crm-field-mapper").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 '{
  "schemaVersion": "1.1",
  "target": "hubspot_v1_1",
  "mapping": [
    {
      "sourceField": "brand_name",
      "targetField": "name",
      "transform": "trim"
    },
    {
      "sourceField": "signal_status",
      "targetField": "signal_status",
      "transform": "lowercase"
    },
    {
      "sourceField": "score",
      "targetField": "signal_score",
      "transform": "number_decimal_2"
    },
    {
      "sourceField": "observed_at",
      "targetField": "last_observed_at",
      "transform": "date_yyyy_mm_dd"
    }
  ],
  "rows": [
    {
      "signalId": "sig-001",
      "entityId": "brand-acme",
      "sourceEvidenceId": "ev-001",
      "sourceUrl": "https://evidence.example.com/acme/offer",
      "observedAt": "2026-08-05T10:00:00Z",
      "values": {
        "brand_name": " Acme ",
        "signal_status": "ACTIVE",
        "score": 82.5
      }
    },
    {
      "signalId": "sig-002",
      "entityId": "brand-acme",
      "sourceEvidenceId": "ev-002",
      "sourceUrl": "https://evidence.example.com/acme/news",
      "observedAt": "2026-08-05T11:00:00Z",
      "values": {
        "brand_name": "Acme",
        "signal_status": "ACTIVE",
        "score": 82.5
      }
    }
  ]
}' |
apify call zinin/us-brand-signal-crm-field-mapper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zinin/us-brand-signal-crm-field-mapper"
        }
    }
}

```

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/YZ7cB1mgcUSK5nXpU/builds/lcFsxHDG0kzMdh3AT/openapi.json
