Intake is a small script you paste into Google Tag Manager once. After that, every form, every lead, and every report can answer the one question that matters: which campaign actually brought this customer?
What's inside this guide: how to install Intake in GTM and verify it works, your first win — sending the real source with every form, a quick-reference of everything Intake tracks, and advanced options for consent, PII hashing, and multi-touch attribution.
Library in one tag, settings in another. Simple to set up, safe to update later.
Paste the full library code from intake.plurio.ai. Don't edit anything here — updates are painless this way.
// Intake library v2.x — paste the whole block from intake.plurio.ai
// DO NOT EDIT. Configuration lives in the "Intake — Config" tag.
!function(t,e){/* …minified library… */}(this, function(){ … });
The tag you actually edit. Start with this recommended config:
intk.init({
// Hash emails/phones from forms (SHA-256) — off by default
pii_collection: { enabled: true },
// Capture GA Client ID for BigQuery stitching — off by default
analytics_ids: { google_analytics: true },
// Push intk_ready event + intk_user_profile to dataLayer
data_layer: true,
// Consent: respect your CMP (OneTrust, Cookiebot, Didomi…)
consent_mode: {
enabled: true,
default_consent: 'denied', // no cookies until user clicks "Accept"
url_passthrough: true // pass UTMs via URL while consent denied
}
});
pii_collection, analytics_ids, and consent_mode are off by default — without them you lose email hashing, GA4 stitching, and GDPR compliance. See the Go Deeper section for domain, social referrals, and more.
Open Config tag → Advanced Settings → Tag Sequencing. Check "Fire a tag before this tag fires" and pick Intake — Library. Turn on "Don't fire this tag if the setup tag fails". Save and publish.
Click Preview in GTM. Visit your site. In the debug panel, look for Intake — Config tag firing on "All Pages". Status: "Tag Fired".
intk_ready in the dataLayerIn the GTM Preview panel, find the intk_ready event in the left sidebar. Click it — you should see intk_user_profile with traffic source data.
intk_ready fires every time Intake finishes loading. Any GTM tag that needs Intake data should use this as its trigger — not "Page View".
Open DevTools (F12) and type intk.get.current.src. You should see your traffic source — (direct) if you typed the URL, google if you came from search.
The highest-value use case: your CRM stops saying "direct / none" and starts showing the actual campaign behind every lead.
1Create a trigger for intk_ready
In GTM: Triggers → New → Custom Event. Name: Intake Ready. Event name: intk_ready. Save.
intk_ready event tells GTM "the data is ready now." If you read variables on Page View, they may still be empty.
2Create GTM Variables
Go to Variables → User-Defined Variables → New. Choose Data Layer Variable. Fill in the name from the table below.
| Name it in GTM | Data Layer Variable Name | Gives you |
|---|---|---|
| Intake — Current Source | intk_user_profile.traffic_attribution.current_visit.source | Visit source |
| Intake — Current Medium | intk_user_profile.traffic_attribution.current_visit.medium | Channel |
| Intake — Current Campaign | intk_user_profile.traffic_attribution.current_visit.campaign | Campaign |
| Intake — First Source | intk_user_profile.traffic_attribution.first_visit.source | Original source |
| Intake — First Date | intk_user_profile.traffic_attribution.first_visit.timestamp | First visit date |
| Intake — Google Click ID | intk_user_profile.identity.click_ids.google | gclid |
intk.get.current.src, intk.get.first.src, etc. Both work. Data Layer Variables are recommended because they pair naturally with intk_ready.
3Pass the data with every form submission
Create a Custom HTML tag in GTM with trigger Intake Ready. It automatically injects hidden fields into every form on the page:
// Fires on trigger: Intake Ready
document.querySelectorAll('form').forEach(function(form) {
var fields = {
'utm_source': intk.get.current.src,
'utm_campaign': intk.get.current.cmp,
'first_source': intk.get.first.src,
'gclid': (intk.get.click_ids || {}).gclid || ''
};
Object.keys(fields).forEach(function(name) {
var input = form.querySelector('input[name="' + name + '"]');
if (!input) {
input = document.createElement('input');
input.type = 'hidden';
input.name = name;
form.appendChild(input);
}
input.value = fields[name];
});
});
The script finds every <form> on the page, creates hidden fields if they don't exist yet, and fills them with Intake data. When the user submits, the values go to your CRM automatically.
In your GA4 Event tag, add the GTM variables from the table above as event parameters:
| Parameter name | Value |
|---|---|
| utm_source | {{Intake — Current Source}} |
| utm_campaign | {{Intake — Current Campaign}} |
| first_source | {{Intake — First Source}} |
| first_date | {{Intake — First Date}} |
| gclid | {{Intake — Google Click ID}} |
Most marketers only see the last click before conversion. With Intake you can send the first click too — and finally answer "which channel starts sales, and which one closes them?"
Add two custom dimensions to your GA4 conversion event tag:
first_touch = {{Intake — First Source}} / {{Intake — First Medium}}
last_touch = {{Intake — Current Source}} / {{Intake — Current Medium}}
(Create Intake — First Medium: Data Layer Variable = intk_user_profile.traffic_attribution.first_visit.medium)
Every path below is a value you can read via GTM variables or in the console.
| JavaScript path | DataLayer path (intk_user_profile) | = |
|---|---|---|
intk.get.current.src | .traffic_attribution.current_visit.source | utm_source |
intk.get.current.mdm | .traffic_attribution.current_visit.medium | utm_medium |
intk.get.current.cmp | .traffic_attribution.current_visit.campaign | utm_campaign |
intk.get.current.cnt | .traffic_attribution.current_visit.content | utm_content |
intk.get.current.trm | .traffic_attribution.current_visit.term | utm_term |
| JavaScript path | DataLayer path | Description |
|---|---|---|
intk.get.first.src | .traffic_attribution.first_visit.source | The very first source |
intk.get.first.mdm | .traffic_attribution.first_visit.medium | The very first channel |
intk.get.first.cmp | .traffic_attribution.first_visit.campaign | The very first campaign |
intk.get.first_add.fd | .traffic_attribution.first_visit.timestamp | Date/time of the first visit |
intk.get.first_add.ep | .traffic_attribution.first_visit.landing_page | First landing page URL |
| JavaScript path | Description |
|---|---|
intk.get.udata.vst | How many times this person has visited your site |
intk.get.session.pgs | Pages seen in the current session |
| JavaScript path | DataLayer (.identity.click_ids) | Platform |
|---|---|---|
intk.get.click_ids.gclid | .google | Google Ads |
intk.get.click_ids.fbclid | .facebook | Meta / Facebook |
intk.get.click_ids.ttclid | .tiktok | TikTok Ads |
intk.get.click_ids.msclkid | .microsoft | Microsoft / Bing |
intk.get.click_ids.li_fatid | .linkedin | LinkedIn Ads |
intk.get.click_ids.twclid | .twitter | Twitter / X |
intk.get.click_ids.snapclid | .snapchat | Snapchat Ads |
intk.get.click_ids.pclid | .pinterest | Pinterest Ads |
intk.get.click_ids.dclid | — | Google DV360 |
intk.get.click_ids.wbraid | — | Google Ads (web-to-app) |
intk.get.click_ids.gbraid | — | Google Ads (app-to-web) |
The config above handles 80% of setups. Below are options you add when you hit a specific problem.
If you run a CMP (OneTrust, Cookiebot, Didomi…) and don't tell Intake, it will set cookies before consent — a compliance risk.
consent_mode: {
enabled: true, // wait for consent before storing cookies
default_consent: 'denied', // no cookies until "Accept"
url_passthrough: true // pass UTMs via URL while consent denied
}
Supports 10+ CMPs. With url_passthrough: true, UTMs and click IDs still travel between pages via the URL.
SPA tracking is on by default. If your site is NOT an SPA and you see duplicate views: spa_tracking: false. Hash-based routing: call intk.trackPageview() manually.
user_id: { source: 'dataLayer', key: 'userId' }
Or set programmatically after login: intk.setUserId('user_123')
Copy into your Config tag and uncomment what you need. Based on a real production setup.
intk.init({
// ── [CUSTOMIZE] Domain ──────────────────────────
domain: 'YOUR_DOMAIN.com',
// ── Identity & Analytics ────────────────────────
pii_collection: { enabled: true },
analytics_ids: { google_analytics: true },
// ── Social traffic classification ─────────────
referrals: [
{ host: 'facebook.com', medium: 'social', display: 'facebook' },
{ host: 'instagram.com', medium: 'social', display: 'instagram' },
{ host: 'linkedin.com', medium: 'social', display: 'linkedin' },
{ host: 'x.com', medium: 'social', display: 'twitter' },
{ host: 'youtube.com', medium: 'social', display: 'youtube' },
{ host: 'tiktok.com', medium: 'social', display: 'tiktok' }
],
// ── Consent — uncomment if you use a CMP ─────
// consent_mode: { enabled: true, default_consent: 'denied', url_passthrough: true },
// ── Uncomment what you need ────────────────────
// user_id: { source: 'dataLayer', key: 'userId' },
// spa_tracking: false,
// timezone_offset: 1,
});
| Feature | How to use it |
|---|---|
| Multi-touch attribution | intk.getAttribution('u-shaped') — 5 models: first, last, linear, U-shaped, time-decay |
| Touchpoint chain | intk.get.touchpoints — the full journey across visits |
| PII capture events | intk_email / intk_phone dataLayer events when forms capture contact info |
| Cross-domain links | link_decoration config — decorate outbound links with UTM + click IDs |
| Consent withdrawal | intk.withdrawConsent() — clears all Intake cookies instantly |
Get the snippet & docs → intake.plurio.ai
Free & open-source → MIT License · no paid tiers, no tracking of you.
Supported CMPs: OneTrust, Cookiebot, Axeptio, Didomi, Sirdata, Termly, TrustArc, Iubenda, Klaro, Consentmo.