# Offline Conversion Upload History Evidence (`h_murdock/offline-conversion-history-evidence`) Actor

Compare current GCLID conversion rows with explicit prior upload outcomes. Preserve duplicate identity/payload conflicts, accepted-history collisions, rejected-only evidence and scoped opaque order drift. JSON or mapped CSV; no Google access or upload.

- **URL**: https://apify.com/h_murdock/offline-conversion-history-evidence.md
- **Developed by:** [Gilad Ronen](https://apify.com/h_murdock) (community)
- **Categories:** Marketing, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.25 / completed report

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?

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

## Offline Conversion Upload History Evidence

Compare a current Google Ads GCLID conversion batch with your authorized previous upload-result records. The report preserves exact identity repeats, differing value or currency payloads, earlier accepted-history collisions, and changed timestamps or click IDs for the same opaque order reference.

This workflow is useful when you upload recurring CRM exports, revisit a backfill, or investigate which submitted rows overlap a previous batch. It operates on the records you supply. It does not connect to Google Ads or upload conversions, and it cannot explain every difference between submitted rows and Ads reporting.

### What to supply

Provide a `snapshotId`, explicit `historyCoverage`, a nonempty `current` array, and a `history` array. Empty history is allowed. Each current row contains string `rowId`, `accountId`, `conversionName`, `gclid`, `conversionTime`, `value`, and `currency`. `orderId` is optional. History rows require those same fields plus an explicit `outcome`: `accepted`, `rejected`, `pending`, or `unknown`.

Use stable account and conversion-action names consistently across the comparison. IDs remain exact strings, including leading zeros, case, and whitespace. Do not provide email addresses, phone numbers, names, or enhanced-conversion contact data. Submit only synthetic or authorized opaque references.

Times must use strict ISO seconds with an explicit `Z` or known UTC offset, such as `2026-10-01T10:00:00+02:00`. Up to three fractional digits are accepted. Equivalent instants compare together while the original time is retained. Values must be decimal strings, not JSON numbers, exponent notation, or locale-formatted amounts. Up to 24 integral and six fractional digits are accepted. Negative values are syntactically supported without asserting that Google will accept them. Currency must contain three uppercase letters; the report checks this syntax, not platform currency support.

For CSV, remove the corresponding array and provide `currentCsv` or `historyCsv`. Canonical headers match the field names. Exact configurable mappings are available through `currentColumns` and `historyColumns`; `orderId` may be absent, and a blank CSV order reference means omitted. BOM and quoted fields are supported. Duplicate, unknown, or missing required headers and irregular rows are rejected. History-only header CSV represents explicit empty history.

### How to read the evidence

The identity comparison is exact account, conversion name, GCLID, and canonical conversion-time instant. Google documents a GCLID/name/time tuple for legacy file deduplication; this report adds your account scope to avoid cross-account merging. It does not model the Google Ads API's separate `order_id` behavior.

Only a supplied `accepted` history label establishes evidence of a previously accepted identity. Rejected-only, pending, unknown, or missing history does not establish acceptance or prove that a conversion is new. `historyCoverage` describes this submitted comparison only, even when set to `complete`. Outcomes are your labels and are never verified against live Google state.

Repeated current identities retain every source reference. Payload values are compared exactly after decimal normalization, without floating-point rounding. A same-order reference with a changed GCLID or time in the same account and conversion name is an order-drift review signal, not proof of a duplicate sale. Different accounts and actions remain separate. The report does not infer click age, GCLID ownership, attribution, delivery, consent, platform acceptance, or adjustment eligibility.

### Results and limits

One dataset item contains the complete report, summary, supplied rows, and evidence rows. Download full JSON, all-evidence CSV, review/unresolved CSV, and escaped standalone HTML. CSV cells beginning with spreadsheet formula markers are neutralized. Use the HTML download link and open the file locally.

The maximum is 2,000 combined current and history rows and 4 MB total input JSON. Identifiers are limited to 160 characters. Full JSON must remain below 8 MB and each export below 9 MB; highly repetitive or long data may require smaller batches. There are no external calls or automatic corrective writes. Inputs and outputs are stored in your Apify run storage, so apply your own access and retention settings.

### Pricing

The intended Store configuration is $0.25 per completed report with platform usage included. One completed report uses one `report-completed` event. Invalid input, insufficient budget, or output-cap rejection is stopped before that report event. Resuming the same run restores its saved report and exports without a second report item or charge. A fresh run is another report and is billable again. Check the Store's current pricing panel before running.

### References

[Google's offline-import FAQ](https://support.google.com/google-ads/answer/10029210?hl=en-GB) explains the legacy tuple and use of upload-result records. [Google Ads API offline conversions](https://developers.google.com/google-ads/api/docs/conversions/upload-offline) documents the separate API workflow. This Actor produces supplied-data evidence for human review and does not certify a Google account or conversion process.

# Actor input Schema

## `snapshotId` (type: `string`):

Opaque reference for this reproducible supplied-data report, max160characters.

## `historyCoverage` (type: `string`):

Required caller declaration for this submitted comparison only. Never asserts Google population coverage. none requires empty history. complete/partial allow explicitly empty history, which cannot prove that a conversion is new.

## `current` (type: `array`):

Each row: unique rowId,exact nonblank string accountId/conversionName/gclid; conversionTime strict ISO seconds with Z or known UTC offset, max3 fractional digits; optional nonblank opaque orderId; value decimal STRING with up to24 integral/6 fractional digits, optional minus; currency3 uppercase letters (syntax only, not currency support). No contact data. Exact string IDs preserve leading zeros/case/whitespace. Max160characters per ID. Invalid calendar/date/currency syntax rejected. Supply nonempty array OR nonempty currentCsv exclusively; remove array when using CSV. Max2000 combined current/history rows and4MB totalJSON.

## `history` (type: `array`):

Each row: unique rowId,exact nonblank string accountId/conversionName/gclid; conversionTime strict ISO seconds with Z or known UTC offset, max3 fractional digits; optional nonblank opaque orderId; value decimal STRING with up to24 integral/6 fractional digits, optional minus; currency3 uppercase letters (syntax only, not currency support). No contact data. Exact string IDs preserve leading zeros/case/whitespace. Max160characters per ID. Invalid calendar/date/currency syntax rejected. Plus explicit outcome accepted/rejected/pending/unknown. Only supplied accepted labels establish prior acceptance evidence; labels are not checked live. Supply array (including \[]) OR nonempty historyCsv exclusively. Remove array for CSV.

## `currentCsv` (type: `string`):

Alternative to current array; remove the array. UTF8/BOM and quoted fields accepted. Required headers rowId/accountId/conversionName/gclid/conversionTime/value/currency; orderId optional. Blank orderId means omitted. Unknown/duplicate headers and irregular rows rejected.

## `historyCsv` (type: `string`):

Alternative to history array; remove the array. Current fields plus required outcome accepted/rejected/pending/unknown. Header-only CSV explicitly represents empty history. Same strict CSV/row validation; no automatic outcome inference.

## `currentColumns` (type: `object`):

Exact mapping of all8 canonical field names to distinct source headers. orderId header may be absent in CSV. Custom mapping requires CSV; canonical mapping is harmless with arrays. No extra/unmapped columns.

## `historyColumns` (type: `object`):

Exact mapping of all9 canonical field names (including outcome) to distinct source headers. orderId header may be absent. Custom mapping requires CSV.

## Actor input object example

```json
{
  "snapshotId": "upload-october-04",
  "historyCoverage": "partial",
  "current": [
    {
      "rowId": "001",
      "accountId": "0001",
      "conversionName": "MQL",
      "gclid": "click-a",
      "conversionTime": "2026-10-01T10:00:00+02:00",
      "orderId": "lead-1",
      "value": "010.00",
      "currency": "USD"
    },
    {
      "rowId": "002",
      "accountId": "0001",
      "conversionName": "MQL",
      "gclid": "click-a",
      "conversionTime": "2026-10-01T08:00:00Z",
      "orderId": "lead-1",
      "value": "10",
      "currency": "USD"
    },
    {
      "rowId": "003",
      "accountId": "0001",
      "conversionName": "MQL",
      "gclid": "click-b",
      "conversionTime": "2026-10-02T08:00:00Z",
      "orderId": "lead-2",
      "value": "12.50",
      "currency": "USD"
    },
    {
      "rowId": "004",
      "accountId": "0001",
      "conversionName": "MQL",
      "gclid": "click-c",
      "conversionTime": "2026-10-03T08:00:00Z",
      "orderId": "lead-3",
      "value": "5.00",
      "currency": "USD"
    },
    {
      "rowId": "005",
      "accountId": "0001",
      "conversionName": "Sale",
      "gclid": "click-a",
      "conversionTime": "2026-10-01T08:00:00Z",
      "orderId": "lead-1",
      "value": "20.00",
      "currency": "USD"
    }
  ],
  "history": [
    {
      "rowId": "h-01",
      "accountId": "0001",
      "conversionName": "MQL",
      "gclid": "click-a",
      "conversionTime": "2026-10-01T08:00:00Z",
      "orderId": "lead-1",
      "value": "11.00",
      "currency": "USD",
      "outcome": "accepted"
    },
    {
      "rowId": "h-02",
      "accountId": "0001",
      "conversionName": "MQL",
      "gclid": "click-b",
      "conversionTime": "2026-10-01T08:00:00Z",
      "orderId": "lead-2",
      "value": "12.50",
      "currency": "USD",
      "outcome": "accepted"
    },
    {
      "rowId": "h-03",
      "accountId": "0001",
      "conversionName": "MQL",
      "gclid": "click-c",
      "conversionTime": "2026-10-03T08:00:00Z",
      "orderId": "lead-3",
      "value": "5.00",
      "currency": "USD",
      "outcome": "rejected"
    }
  ],
  "currentColumns": {
    "rowId": "rowId",
    "accountId": "accountId",
    "conversionName": "conversionName",
    "gclid": "gclid",
    "conversionTime": "conversionTime",
    "orderId": "orderId",
    "value": "value",
    "currency": "currency"
  },
  "historyColumns": {
    "rowId": "rowId",
    "accountId": "accountId",
    "conversionName": "conversionName",
    "gclid": "gclid",
    "conversionTime": "conversionTime",
    "orderId": "orderId",
    "value": "value",
    "currency": "currency",
    "outcome": "outcome"
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `json` (type: `string`):

No description

## `evidence` (type: `string`):

No description

## `findings` (type: `string`):

No description

## `html` (type: `string`):

No description

# 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 = {
    "snapshotId": "upload-october-04",
    "historyCoverage": "partial",
    "current": [
        {
            "rowId": "001",
            "accountId": "0001",
            "conversionName": "MQL",
            "gclid": "click-a",
            "conversionTime": "2026-10-01T10:00:00+02:00",
            "orderId": "lead-1",
            "value": "010.00",
            "currency": "USD"
        },
        {
            "rowId": "002",
            "accountId": "0001",
            "conversionName": "MQL",
            "gclid": "click-a",
            "conversionTime": "2026-10-01T08:00:00Z",
            "orderId": "lead-1",
            "value": "10",
            "currency": "USD"
        },
        {
            "rowId": "003",
            "accountId": "0001",
            "conversionName": "MQL",
            "gclid": "click-b",
            "conversionTime": "2026-10-02T08:00:00Z",
            "orderId": "lead-2",
            "value": "12.50",
            "currency": "USD"
        },
        {
            "rowId": "004",
            "accountId": "0001",
            "conversionName": "MQL",
            "gclid": "click-c",
            "conversionTime": "2026-10-03T08:00:00Z",
            "orderId": "lead-3",
            "value": "5.00",
            "currency": "USD"
        },
        {
            "rowId": "005",
            "accountId": "0001",
            "conversionName": "Sale",
            "gclid": "click-a",
            "conversionTime": "2026-10-01T08:00:00Z",
            "orderId": "lead-1",
            "value": "20.00",
            "currency": "USD"
        }
    ],
    "history": [
        {
            "rowId": "h-01",
            "accountId": "0001",
            "conversionName": "MQL",
            "gclid": "click-a",
            "conversionTime": "2026-10-01T08:00:00Z",
            "orderId": "lead-1",
            "value": "11.00",
            "currency": "USD",
            "outcome": "accepted"
        },
        {
            "rowId": "h-02",
            "accountId": "0001",
            "conversionName": "MQL",
            "gclid": "click-b",
            "conversionTime": "2026-10-01T08:00:00Z",
            "orderId": "lead-2",
            "value": "12.50",
            "currency": "USD",
            "outcome": "accepted"
        },
        {
            "rowId": "h-03",
            "accountId": "0001",
            "conversionName": "MQL",
            "gclid": "click-c",
            "conversionTime": "2026-10-03T08:00:00Z",
            "orderId": "lead-3",
            "value": "5.00",
            "currency": "USD",
            "outcome": "rejected"
        }
    ],
    "currentCsv": "",
    "historyCsv": "",
    "currentColumns": {
        "rowId": "rowId",
        "accountId": "accountId",
        "conversionName": "conversionName",
        "gclid": "gclid",
        "conversionTime": "conversionTime",
        "orderId": "orderId",
        "value": "value",
        "currency": "currency"
    },
    "historyColumns": {
        "rowId": "rowId",
        "accountId": "accountId",
        "conversionName": "conversionName",
        "gclid": "gclid",
        "conversionTime": "conversionTime",
        "orderId": "orderId",
        "value": "value",
        "currency": "currency",
        "outcome": "outcome"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("h_murdock/offline-conversion-history-evidence").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 = {
    "snapshotId": "upload-october-04",
    "historyCoverage": "partial",
    "current": [
        {
            "rowId": "001",
            "accountId": "0001",
            "conversionName": "MQL",
            "gclid": "click-a",
            "conversionTime": "2026-10-01T10:00:00+02:00",
            "orderId": "lead-1",
            "value": "010.00",
            "currency": "USD",
        },
        {
            "rowId": "002",
            "accountId": "0001",
            "conversionName": "MQL",
            "gclid": "click-a",
            "conversionTime": "2026-10-01T08:00:00Z",
            "orderId": "lead-1",
            "value": "10",
            "currency": "USD",
        },
        {
            "rowId": "003",
            "accountId": "0001",
            "conversionName": "MQL",
            "gclid": "click-b",
            "conversionTime": "2026-10-02T08:00:00Z",
            "orderId": "lead-2",
            "value": "12.50",
            "currency": "USD",
        },
        {
            "rowId": "004",
            "accountId": "0001",
            "conversionName": "MQL",
            "gclid": "click-c",
            "conversionTime": "2026-10-03T08:00:00Z",
            "orderId": "lead-3",
            "value": "5.00",
            "currency": "USD",
        },
        {
            "rowId": "005",
            "accountId": "0001",
            "conversionName": "Sale",
            "gclid": "click-a",
            "conversionTime": "2026-10-01T08:00:00Z",
            "orderId": "lead-1",
            "value": "20.00",
            "currency": "USD",
        },
    ],
    "history": [
        {
            "rowId": "h-01",
            "accountId": "0001",
            "conversionName": "MQL",
            "gclid": "click-a",
            "conversionTime": "2026-10-01T08:00:00Z",
            "orderId": "lead-1",
            "value": "11.00",
            "currency": "USD",
            "outcome": "accepted",
        },
        {
            "rowId": "h-02",
            "accountId": "0001",
            "conversionName": "MQL",
            "gclid": "click-b",
            "conversionTime": "2026-10-01T08:00:00Z",
            "orderId": "lead-2",
            "value": "12.50",
            "currency": "USD",
            "outcome": "accepted",
        },
        {
            "rowId": "h-03",
            "accountId": "0001",
            "conversionName": "MQL",
            "gclid": "click-c",
            "conversionTime": "2026-10-03T08:00:00Z",
            "orderId": "lead-3",
            "value": "5.00",
            "currency": "USD",
            "outcome": "rejected",
        },
    ],
    "currentCsv": "",
    "historyCsv": "",
    "currentColumns": {
        "rowId": "rowId",
        "accountId": "accountId",
        "conversionName": "conversionName",
        "gclid": "gclid",
        "conversionTime": "conversionTime",
        "orderId": "orderId",
        "value": "value",
        "currency": "currency",
    },
    "historyColumns": {
        "rowId": "rowId",
        "accountId": "accountId",
        "conversionName": "conversionName",
        "gclid": "gclid",
        "conversionTime": "conversionTime",
        "orderId": "orderId",
        "value": "value",
        "currency": "currency",
        "outcome": "outcome",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("h_murdock/offline-conversion-history-evidence").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 '{
  "snapshotId": "upload-october-04",
  "historyCoverage": "partial",
  "current": [
    {
      "rowId": "001",
      "accountId": "0001",
      "conversionName": "MQL",
      "gclid": "click-a",
      "conversionTime": "2026-10-01T10:00:00+02:00",
      "orderId": "lead-1",
      "value": "010.00",
      "currency": "USD"
    },
    {
      "rowId": "002",
      "accountId": "0001",
      "conversionName": "MQL",
      "gclid": "click-a",
      "conversionTime": "2026-10-01T08:00:00Z",
      "orderId": "lead-1",
      "value": "10",
      "currency": "USD"
    },
    {
      "rowId": "003",
      "accountId": "0001",
      "conversionName": "MQL",
      "gclid": "click-b",
      "conversionTime": "2026-10-02T08:00:00Z",
      "orderId": "lead-2",
      "value": "12.50",
      "currency": "USD"
    },
    {
      "rowId": "004",
      "accountId": "0001",
      "conversionName": "MQL",
      "gclid": "click-c",
      "conversionTime": "2026-10-03T08:00:00Z",
      "orderId": "lead-3",
      "value": "5.00",
      "currency": "USD"
    },
    {
      "rowId": "005",
      "accountId": "0001",
      "conversionName": "Sale",
      "gclid": "click-a",
      "conversionTime": "2026-10-01T08:00:00Z",
      "orderId": "lead-1",
      "value": "20.00",
      "currency": "USD"
    }
  ],
  "history": [
    {
      "rowId": "h-01",
      "accountId": "0001",
      "conversionName": "MQL",
      "gclid": "click-a",
      "conversionTime": "2026-10-01T08:00:00Z",
      "orderId": "lead-1",
      "value": "11.00",
      "currency": "USD",
      "outcome": "accepted"
    },
    {
      "rowId": "h-02",
      "accountId": "0001",
      "conversionName": "MQL",
      "gclid": "click-b",
      "conversionTime": "2026-10-01T08:00:00Z",
      "orderId": "lead-2",
      "value": "12.50",
      "currency": "USD",
      "outcome": "accepted"
    },
    {
      "rowId": "h-03",
      "accountId": "0001",
      "conversionName": "MQL",
      "gclid": "click-c",
      "conversionTime": "2026-10-03T08:00:00Z",
      "orderId": "lead-3",
      "value": "5.00",
      "currency": "USD",
      "outcome": "rejected"
    }
  ],
  "currentCsv": "",
  "historyCsv": "",
  "currentColumns": {
    "rowId": "rowId",
    "accountId": "accountId",
    "conversionName": "conversionName",
    "gclid": "gclid",
    "conversionTime": "conversionTime",
    "orderId": "orderId",
    "value": "value",
    "currency": "currency"
  },
  "historyColumns": {
    "rowId": "rowId",
    "accountId": "accountId",
    "conversionName": "conversionName",
    "gclid": "gclid",
    "conversionTime": "conversionTime",
    "orderId": "orderId",
    "value": "value",
    "currency": "currency",
    "outcome": "outcome"
  }
}' |
apify call h_murdock/offline-conversion-history-evidence --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,h_murdock/offline-conversion-history-evidence"
        }
    }
}
```

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/FeINQABP6w1kVIYHg/builds/wwvTHQlCG4KcLYFTK/openapi.json
