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 to near-pixel match rates on ad-platform conversions using only your existing CRM and form code.
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 attribution to ad platforms (≈100% match rate vs. ~30-60% for hashed-PII-only)
- ✅ The "Lead" stage event firing to ad platforms when leads are created in your CRM
The setup takes ~15 minutes and lives entirely on your side — Attribi reads what you populate.
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 Freshsales and LeadSquared, the fields had to be created manually because their APIs don't 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 — much cleaner.
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. Hashed email + phone fallback gives ~30-60% match on ad platforms — good enough for most pixel-less customers.
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 will auto-detect them when CRM-pull next runs (~5 min).
- 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
Like Freshsales, LeadSquared does not support programmatic custom field creation via API. Manual setup:
- 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
After setup, send yourself a test lead:
- Click one of your own Google ads (or paste your landing URL with
?gclid=test_abc123). - Submit the form on the landing page with a real email.
- In your CRM, find the new contact. Confirm
mta_clickid = test_abc123andmta_clickid_type = gclid. - Wait up to ~5 minutes for Attribi's CRM-pull to run.
- In Attribi → Leads, find the lead. Click into the detail view. The touchpoints section should show one entry with
click_id_platform: gclid. - Mark the lead as closed-won (use the button on the lead detail page, or transition the deal stage in your CRM and wait for the next CRM-pull cycle).
- In your Google Ads UI, check Tools → Conversions → Recent uploads. You should see the conversion within a few minutes, attributed to the
test_abc123click.
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 fine without click ID capture — your offline conversions still fire to ad platforms using hashed email + phone matching. Match rates drop from ~100% to ~30-60% depending on the platform, but real revenue is still attributed. Many customers ship with hashed-PII-only 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
Click IDs are not personally identifiable information (PII) — they're random strings that ad platforms use to attribute clicks to their own logged-in users. Storing them in your CRM doesn't change your data-handling posture.
The hashed email + phone fallback that Attribi uses on ad platforms also preserves privacy — only one-way SHA-256 hashes leave your CRM, never raw values.
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 when CRM-pull runs.
- "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-05-05.