# AI lead evidence review — Lonta AI, 2026-10-29

Read the article: https://lonta.io/blog/ai-search-lead-attribution/

Start with office-worksheet.txt. The sample is invented: ten contacts, nine unique jobs. This package reviews supplied records; it does not collect traffic, authenticate AI referrals, connect to accounts, or establish causal attribution.

## Run locally

Extract the entire ZIP into a folder. With Node.js 20 or later:

    node core.mjs
    node core.mjs your-file.json
    node --test core.test.mjs

No dependencies or network requests. Keep real customer files and output private. The sample uses reserved .example domains and synthetic markers, not real people or credentials.

## Input

A JSON array with at most 5000 contact objects:

- contactId: nonempty stable contact ID. An identical retry is ignored; conflicting reuse rejects the whole input.
- jobId: nonempty stable job ID. Repeated contacts about one job count once. Unmatched inquiries need a separate unresolved list; the program rejects missing IDs.
- landing: absolute HTTP(S) captured landing URL, without URL credentials. Do not put customer personal details into example URLs.
- referrer: captured absolute HTTP(S) referrer or empty string if missing.
- reportedAI: true only if a customer affirmatively reported AI assistance. false means no affirmative record; it does not mean the customer denied AI use. Preserve actual wording and uncertainty separately in the private office worksheet.
- excluded: true for a known staff/test/bot record identified by your own process. This is a supplied assertion; the code does not detect bots or tests.
- outcome: paid, pending or lost at one common review cutoff.
- collectedCents: observed nonnegative integer USD cents for paid; null for pending; zero for lost. All contacts for a job must use the same final snapshot, not a series of payment events. Zero paid is a recorded zero, not missing money. Maximum totals must remain safe integers.

## Local classification and hold rules

Exact referrer hosts chatgpt.com or chat.openai.com, or a utm_source value chatgpt.com or chatgpt, are technical markers under this review policy (case-insensitive). No suffix or substring matches. They are supplied strings, not authenticated evidence that an assistant sent a customer. The policy is intentionally narrow and does not classify every assistant or every URL format.

A same-contact AI marker with a paid marker (nonempty gclid, gbraid, wbraid, msclkid; or utm_medium cpc, ppc, paid, paid_search) is held. Paid markers themselves are unverified. Different contacts may legitimately contain different source markers; the reviewer retains them without forcing an exclusive attribution winner.

Repeated utm_source keys, conflicting job outcomes/amounts, or mixed excluded/ordinary contacts hold the entire job. Structural invalid input rejects the whole batch with a nonzero exit; no partial report is printed. Held jobs and excluded jobs are outside the eligible totals.

## Literal sample result

10 contacts; 9 jobs; 1 excluded; 1 held; 7 eligible.
3 technical-evidence jobs + 4 customer-reported jobs - 2 overlapping jobs = 5 in the union.
4 union jobs paid; 1 pending. Collected USD: 500 + 700 + 900 + 1100 = 3200.
Technical-evidence paid amount 1200 + customer-reported paid amount 2500 - overlapping paid amount 500 = 3200.
This is evidence-associated collected money within the sample, not revenue caused by AI.

## Files and rights

core.mjs: original reviewer and CLI, MIT.
core.test.mjs: original calculation/validation tests, MIT.
sample.json and office-worksheet.txt: original synthetic fixture and proposed worksheet, CC BY 4.0.
expected-summary.json: literal expected totals, CC BY 4.0.
LICENSE.txt gives the licenses and attribution. Source platform documentation linked in the article retains its own rights and is not redistributed here.
