Attribution snippet
One site-wide script that records where each visitor came from and fills your forms' hidden fields automatically.
The attribution snippet is the foundation of everything else. Installed once, site-wide, it:
- Records the first touch. On a visitor’s first page view it captures the
UTM parameters, Google click ID (
gclid), referrer and landing page, works out the marketing channel, and stores the result in a cookie (kq_attr, 30 days). Return visits keep the original — attribution is first-touch. - Fills your forms’ hidden fields. On every page it writes the stored values into hidden form inputs (see Form webhook for the field list), so every submission carries its own attribution.
- Sends a beacon fallback. When a visitor shows form intent (focuses a field, or a form scrolls into view), it sends the attribution data to your instance so calls and emails can be matched by IP and browser later, even without a form submission. Throttled to once per 30 minutes per session.
- Powers the email enquiry link. The mailto snippet reads the same cookie to inject the page URL and click ID into the enquiry email body.
Your personalised copy of the snippet — with your instance’s beacon URL already baked in — is in the app under Settings → Setup Guide → section 1. The Setup Guide also has a live checker that verifies the snippet is installed and beaconing correctly on your site.
Installing it
Paste the snippet just before the closing </body> tag on every page of
your site. In most CMSs that means the site-wide footer template or a
“custom scripts” box. If your site runs WordPress, there is also a small
plugin version of the snippet — ask your Syft admin.
That’s the whole install. The snippet has no visible output and doesn’t slow the page — it’s a few kilobytes of plain JavaScript with no dependencies.
How the channel is worked out
The snippet classifies each first visit into a channel, in this priority order:
| Signal on first visit | Channel | Drill-down |
|---|---|---|
gclid present | Paid Search | |
utm_medium is cpc / ppc / paid_search | Paid Search (or Paid Social if the source is a social network) | Source engine/network |
utm_medium is paid_social | Paid Social | Network |
| UTM source/medium = email | Source | |
| Other UTMs from a social network | Organic Social | Network |
| Other UTMs | Referral | Source |
| No UTMs, referrer is a search engine | Organic Search | Engine |
| No UTMs, referrer is a social network | Organic Social | Network |
| No UTMs, other referrer | Referral | Referrer host |
| Nothing | Direct | — |
Campaign lands in drill-down 2 and ad content / keyword in drill-down 3. The
snippet also generates a unique transaction_id per visitor, used by the
Google Ads value-restatement integration.
How the snippet finds your form fields
You don’t wire anything up manually — add hidden fields to your form and the snippet finds them, matching in three ways (in order):
- By field name —
name="channel",name="gclid", etc. - By CSS class —
class="kq-attr-channel"— for form builders that don’t let you set the input name directly (e.g. Gravity Forms). - By data attribute —
data-kq-attr="channel".
Two extras for compatibility:
- Standard UTM names work too. Fields named
utm_source,utm_medium,utm_campaign,utm_term,utm_contentare filled with the corresponding channel values. - Attributer-style placeholders work. An input whose default value is
[channel](or any other known key in square brackets) gets replaced. This makes Syft a drop-in replacement for Attributer.io — existing forms keep working without changes.
Forms added to the page after load (modals, lazy-loaded blocks) are caught automatically.
The snippet
For reference, this is what the snippet does — but don’t copy it from here.
Use the pre-filled version from your Setup Guide, which has your instance’s
beacon URL in place of https://YOUR-INSTANCE/api/attribution.
<script>
(function () {
var COOKIE = 'kq_attr', DAYS = 30;
var BEACON_URL = 'https://YOUR-INSTANCE/api/attribution';
// 1. On first visit: read UTMs, gclid, referrer + landing page,
// classify the channel, store everything in the kq_attr cookie.
// 2. On every page: fill hidden form fields (by name, kq-attr-* class,
// data-kq-attr attribute, or [placeholder] value) with the stored data.
// 3. On form intent: beacon the data to your instance as a fallback
// matching path for calls and emails (30-minute session throttle).
/* full implementation supplied by your Setup Guide */
})();
</script>
Checking it works
In the app, Setup Guide → section 1 has a snippet checker: give it a page
URL on your site and it reports whether the page is reachable, whether the
kq_attr cookie gets set, whether the beacon fires to the right URL, and
whether recent beacons have been received. Use it after install and after any
site redesign.