Pixel-less Mode Setup Guide
Run Attribi without installing the website tracker. Use your CRM as the source of truth and capture click IDs via existing forms.
For customers who want to use Attribi without installing tracker.js.
If your IT team is taking time to approve the tracker install, or you have a no-third-party-JavaScript policy, this guide gets you click-level matching on ad-platform conversions using only your existing CRM and form code, for the CRMs that can hand the click ID back (HubSpot, Pipedrive, Zoho, Freshsales and LeadSquared; Salesforce is the exception, see below).
What you get without the tracker
When you connect a CRM and one or more ad platforms, Attribi already does the following automatically — no extra setup needed:
- ✅ Imports leads from your CRM into Attribi
- ✅ Pushes qualified + closed-won offline conversions to your ad platforms (with hashed email + phone)
- ✅ Enriches your CRM contacts with attribution data (
mta_first_touch_source,mta_attribution_path, etc.)
With the steps in this guide, you also get:
- ✅ Click-level matching on ad-platform uploads. A click ID is matched deterministically by the platform that issued it; hashed email and phone are matched probabilistically against logged-in users, so a lower and platform-dependent share of those uploads is attributed. Neither share is something we can quote as a number.
- ✅ The "Lead" stage event firing to ad platforms when leads first appear in your CRM (qualified and closed-won fire either way)
The setup takes about 15 minutes and lives entirely on your side. Attribi reads what you populate on its next scheduled CRM read, which runs once a day.
How it works
When someone clicks one of your ads:
- They land on your website with a click ID in the URL —
?gclid=ABC123(Google),?fbclid=xyz(Meta), etc. - Your existing form on the landing page should capture that click ID and submit it to your CRM as a custom field.
- Attribi reads that custom field from your CRM and forwards the click ID to the right ad platform when the lead converts.
You need to populate two custom fields on each new CRM contact:
| Field | Holds |
|---|---|
mta_clickid | The click ID value (e.g., EAIaIQobChMI...) |
mta_clickid_type | The URL parameter name. One of: gclid, wbraid, gbraid, fbclid, li_fat_id, ttclid, msclkid, ScCid |
Attribi already provisioned these fields when you connected your CRM (HubSpot, Salesforce, Pipedrive, Zoho — see your CRM's custom fields panel). For LeadSquared, Attribi creates them through LeadSquared's field API when you click Provision (since 2026-10-12); for Freshsales the fields have to be created manually, because its API does not allow programmatic creation. See your connection's status in Attribi for details.
The rest of this guide shows how to populate those fields from your form code.
Universal JavaScript snippet — works for any form
The simplest approach: add this script anywhere on your landing pages before your form. It runs once on page load, finds any click ID in the URL, and stores it. When your form is submitted, the script writes the click ID into hidden fields named mta_clickid and mta_clickid_type.
<script>
(function () {
// Order matters — Google's wbraid/gbraid coexist with gclid in some campaigns,
// but never overlap with other platforms. The first match wins.
var paramOrder = ['gclid', 'wbraid', 'gbraid', 'fbclid', 'li_fat_id', 'ttclid', 'msclkid', 'ScCid'];
var params = new URLSearchParams(window.location.search);
var found = paramOrder.find(function (p) { return params.get(p); });
if (found) {
// Store in localStorage so we can populate the form even if the user
// navigated to a different page before submitting.
try {
localStorage.setItem('mta_clickid', params.get(found));
localStorage.setItem('mta_clickid_type', found);
} catch (e) { /* localStorage may be disabled in private mode */ }
}
// On form submit, ensure both hidden fields are populated.
document.addEventListener('submit', function (e) {
var form = e.target;
var stored = (function () {
try {
return {
id: localStorage.getItem('mta_clickid'),
type: localStorage.getItem('mta_clickid_type'),
};
} catch (e) { return { id: null, type: null }; }
})();
if (!stored.id || !stored.type) return;
function setHidden(name, value) {
var existing = form.querySelector('input[name="' + name + '"]');
if (!existing) {
existing = document.createElement('input');
existing.type = 'hidden';
existing.name = name;
form.appendChild(existing);
}
existing.value = value;
}
setHidden('mta_clickid', stored.id);
setHidden('mta_clickid_type', stored.type);
}, true);
})();
</script>
This snippet is form-builder-agnostic. It works for HubSpot Forms, Marketo Forms, Pardot, Pipedrive WebForms, custom HTML forms, and any other form that posts standard <input> values to your CRM.
For most customers, this snippet alone is enough. The per-CRM sections below cover form-builder-specific gotchas if you hit any.
Per-CRM specifics
HubSpot Forms (most common B2B path)
HubSpot Forms internally maps form fields to contact properties using the property's internal name. The custom fields Attribi provisioned use these internal names:
mta_clickid(text)mta_clickid_type(text)
Steps:
- In HubSpot, go to Contacts → Properties and verify the two
mta_*properties exist (Attribi provisioned them automatically — they appear in the "Attribi MTA" property group). - Edit your form (or create a new one). Add two hidden contact properties to the form:
mta_clickidmta_clickid_type
- Embed the form on your landing page. The universal snippet above will populate the hidden fields automatically.
If you use HubSpot's JavaScript embed API (hbspt.forms.create), the hidden fields are populated by the universal snippet. No HubSpot-specific code needed.
Salesforce Web-to-Lead
Salesforce's Web-to-Lead generator outputs HTML forms where field names are auto-generated IDs (e.g., 00N5g0000098xyz) — not your CRM-API field names. To populate MTA_clickid__c:
- In Salesforce, go to Setup → Object Manager → Lead → Fields & Relationships. Find
MTA_clickid__candMTA_clickid_type__c(Attribi provisioned them automatically). - Click the field name → copy the auto-generated ID from the URL (after
&id=). - In your Web-to-Lead form HTML, add hidden inputs using those IDs as the name:
Replace<input type="hidden" id="MTA_clickid__c" name="00N5g0000098xyz" value="" /> <input type="hidden" id="MTA_clickid_type__c" name="00N5g00000ABCDEF" value="" />00N5g0...with the actual IDs from step 2. - Adapt the universal snippet to write to those IDs:
setHidden('00N5g0000098xyz', stored.id); // mta_clickid setHidden('00N5g00000ABCDEF', stored.type); // mta_clickid_type
If your tier supports it, Salesforce Lightning Forms (modern replacement) lets you use API field names directly, which is much cleaner.
Salesforce limit, read before you build this. The two fields are provisioned on the Lead object. When Salesforce converts the Lead, the Contact that the won Opportunity points at does not carry them, and Attribi's Salesforce read matches revenue on the contact's email and phone only. Capturing the click ID into Salesforce today keeps it for your own reporting; Attribi does not use it for matching yet. The Salesforce integration page lists this under "On the roadmap". Installing tracker.js is what gives Salesforce customers click-level matching now.
Pipedrive WebForms
Pipedrive's custom fields have hash-ID keys (e.g., f47ac10b58cc4372a567...), not human names. Attribi stored these IDs in your connection metadata — you can find them in the Pipedrive admin Field Manager or by hovering over the field name.
If you use Pipedrive's hosted WebForm: Pipedrive doesn't currently support populating custom fields via URL parameters. Workarounds:
- Best: use a tag manager (GTM) to capture click IDs into cookies, and submit a parallel API call to Pipedrive's REST API on form submit. We're investigating a cleaner integration in a future release.
- Simplest: skip click ID capture for Pipedrive. The hashed email and phone fallback still uploads every qualified and won outcome; the platform decides how many it can match, and that share is lower than with a click ID.
Zoho CRM
Zoho's custom fields use the literal names (mta_clickid, mta_clickid_type).
Zoho Forms: edit form → add two hidden fields named exactly mta_clickid and mta_clickid_type. The universal snippet populates them.
Custom forms posting to Zoho: include mta_clickid and mta_clickid_type as form field names. They map directly to the Zoho contact module fields.
Freshsales
Freshsales prefixes custom fields with cf_. The fields are:
cf_mta_clickidcf_mta_clickid_type
Note: Freshsales does not support programmatic custom field creation via API, so Attribi could not auto-provision these. You need to create them manually:
- In Freshsales, go to Admin → Customize Module → Contacts → Custom Fields.
- Create both fields as Text (single-line). Attribi reads them on its next daily CRM run; nothing else to configure.
- In your form, populate hidden fields named exactly
cf_mta_clickidandcf_mta_clickid_type.
If you don't want to create the fields, pixel-less mode still works without them — Attribi falls back to hashed-email + phone match.
LeadSquared
LeadSquared prefixes custom fields with mx_. Conventionally, MTA fields use:
mx_MTA_clickidmx_MTA_clickid_type
Attribi creates the LeadSquared fields through its documented CreateLeadField API when you click Provision (since 2026-10-12). If your account answers that call with 404, the manual setup below applies:
- In LeadSquared, go to Settings → Customization → Lead Fields → Add Custom Field.
- Create both fields as Text (Schema name
mx_MTA_clickid/mx_MTA_clickid_type). - In your form, populate hidden fields named exactly
mx_MTA_clickidandmx_MTA_clickid_type.
Verifying it works
There are two different tests here. Keep them apart.
Test 1: does the field reach the CRM? This can use a made-up value.
- Open your landing URL with
?gclid=test_abc123and submit the form with a test email address you will delete afterwards. - In your CRM, find the new contact. Confirm
mta_clickid = test_abc123andmta_clickid_type = gclid. - Delete or exclude that test contact before it reaches a qualified or won stage. A fabricated GCLID is not an ad click: Google will not attribute anything to it, and a test record that reaches closed-won would be uploaded as a real conversion with that invalid ID.
Test 2: does the click ID survive to the platform? This needs a real click from your own ad account and a real outcome.
- Click one of your own Google ads so the landing URL carries a real
gclid, and submit the form with an address you can identify later. - Confirm the contact in your CRM carries the real click ID in
mta_clickid. - Wait for Attribi's next daily CRM read.
- In Attribi, open Leads and find the lead. Its touchpoints section should show one entry with
click_id_platform: google(the CRM stores the parameter namegclid; Attribi translates it to the platform name). - Move that record to the stage you mapped as qualified, or mark it closed-won, and wait for the next daily read.
- In Google Ads, open Goals → Conversions → Uploads (older accounts: Tools → Conversions → Uploads) and look for the upload. Google's own guidelines say imported conversions take about 3 hours to appear in reporting. Remove the test outcome from your CRM afterwards so it does not stay in your revenue figures.
If the touchpoint doesn't appear in Attribi: check that the custom field is populated in your CRM (most common issue is the field name mismatch — re-check the universal snippet's mta_clickid / mta_clickid_type names match your CRM's field names exactly, including the cf_ / mx_ / __c prefixes if applicable).
What if I don't do any of this?
Pixel-less mode works without click ID capture: your offline conversions still upload to ad platforms with hashed email and phone. Each platform then matches those hashes against its own logged-in users, and attributes a lower share than it would with a click ID; the share depends on the platform and on how many of your contacts gave the same email or phone to it. One Google-specific condition: Google accepts hashed identifiers only after you have accepted its Enhanced Conversions terms inside your Google Ads account. Until then Google rejects uploads that carry only hashed identifiers, and a pixel-less setup has no click ID to fall back on. Many customers ship with hashed identifiers as a starting point and add click ID capture later.
If you eventually install Attribi's tracker.js, you don't need to remove this setup — the tracker takes precedence and captures full multi-touch attribution. The CRM custom fields stay populated as a fallback.
Privacy note
A click ID is a pseudonymous token the ad platform issued for one click. It identifies the click to that platform, not a person to you. Storing it in your CRM is a data-handling decision for your privacy owner, like any other field you keep about a contact.
The hashed email and phone fallback sends one-way SHA-256 hashes to the ad platforms, never raw values. Hashing does not replace consent: the platforms' own policies still require the disclosures and permissions that apply to your customers.
Questions or troubleshooting
- "My form posts to a third-party endpoint, not the CRM directly" — populate the hidden fields anyway. As long as the CRM contact ends up with the values populated (via Zapier, Make, or your own integration), Attribi reads them on its next daily CRM run.
- "My CRM has multiple form sources" — every form should populate the same two custom fields. Attribi tolerates last-write-wins semantics; the most recent click ID per contact is what gets attributed.
- "What if a user clicks two different ads before submitting?" — the universal snippet uses
localStorageand last-write-wins. The most recent click ID stored is what gets submitted. For full multi-touch attribution across ad platforms, installtracker.js. - "Where can I see if pixel-less mode is active?" — your Attribi connections page shows a yellow banner when the tracker is not detected. Once you install the tracker, the banner disappears within 7 days of the next pageview.
Last updated 2026-10-13.
Follow this guide in your own workspace.
Start a free Attribi workspace, connect your platforms, and set this up as you read.
No credit card. Free up to 500 visitors a month. New workspaces start with 30 days on Pro.