---
url: /recipes/set-up-tracking-and-consent.md
---
# Set up tracking and consent

From the client's accounts to a live site whose tags fire with the visitor's consent, with one test run on staging in between. Written against 2.18.0.

::: glance

* **You need** access to the client's Google Tag Manager, GA4 and, where used, Meta, LinkedIn, Pinterest and Google Ads accounts, plus a developer login on the site.
* **You get** one GTM container with the platform tags, Consent Mode v2 with the policy the client chose, the two server-side conversions, and the checks that keep it working.
* **Pages** [Tracking and Consent](/site/tracking-and-consent), [Google Tag Manager](/integrations/google-tag-manager), [Settings](/panel/settings), [Health Checks](/site/health-checks)
  :::

## 1. Decide the consent policy with the client

Evolve implements Google Consent Mode v2 with two categories, analytics and marketing. Three settings on the Consent card of Settings > Tracking decide how they start and who asks the visitor:

| Decision | Setting |
|---|---|
| Granted until the visitor declines (`opt-out`, the Swiss default under the DSG) or denied until the visitor accepts (`opt-in`, as the GDPR requires) | Consent policy |
| Show the Evolve cookie banner | Cookie banner |
| The client runs a consent platform (Cookiebot, Usercentrics) in the GTM container instead | Consent via CMP |

The panel refuses opt-in with neither the banner nor the CMP option on, because nobody could grant consent. With the banner off and `opt-out`, everything is granted and nobody can decline. That is a decision for the site owner, not a default to leave unexamined. Record the choice in the project notes.

**Check:** view the source of any page. The inline consent script near the top of `<head>` calls `gtag('consent', 'default', ...)` with `granted` or `denied` matching the policy. It renders on every environment.

## 2. Collect the IDs

| Platform | Where to find the ID |
|---|---|
| GA4 | Analytics Admin > Data Streams > Web, "Measurement ID" (`G-...`) |
| Meta pixel | Events Manager > Data Sources, the pixel's ID |
| LinkedIn | Campaign Manager > Analyze > Insight Tag, partner ID; conversions under Conversion Tracking |
| Pinterest | Ads > Conversions > Pinterest Tag, tag ID |
| Google Ads | Goals > Conversions, each action's conversion ID and label |

For the server-side conversions you also need the GA4 Measurement Protocol API secret (Analytics Admin > Data Streams > Web > Measurement Protocol API secrets) and the Meta Conversions API access token (Events Manager > Settings of the pixel).

## 3. Build the container

1. Create a Web container for the site in GTM.
2. Import `vendor/racerfish/evolve/theme/gtm_container.json` under Admin > Import Container, into a new workspace, merge. It brings the tags, triggers and variables for GA4, the Meta pixel, LinkedIn Insight, Pinterest and Google Ads, wired to the events the theme pushes.
3. Edit the platform variables with the IDs from step 2. Delete the tags of platforms the client does not use.
4. Keep the consent checks on the tags. Evolve sets the Consent Mode default before GTM loads and the banner updates it; the tags wait for that.

Do not publish yet; step 5 previews the workspace first.

## 4. Put the IDs on the site

In the panel under Settings > Tracking:

| Card | Fields |
|---|---|
| Google | GTM container, GA4 measurement ID, conversion data (settings store); `ANALYTICS_API_SECRET` (`.env`, secret, masked) |
| Facebook / Meta | pixel ID (settings store); `FACEBOOK_CONVERSIONS_API_KEY` (`.env`, secret, masked), `FACEBOOK_TEST_EVENT_CODE` (`.env`) for the test run |
| Server-side tracking | server-side events stay on or inherit; drivers without credentials disable themselves |
| Pinterest, LinkedIn, Hotjar | tag ID, partner and lead conversion IDs, site ID where used |

The public IDs go into the [settings store](/panel/settings-store), the secrets and the debugging switches into the `.env` of the environment the panel runs on. A database copy carries the stored IDs to staging; that is harmless, because a staging site renders no GTM snippet and sends no conversions. On a site whose `.env` still carries `GTM_ID` and friends, the panel shows those as placeholders and an import hint; run `php artisan evolve:settings:import` once so the store holds them.

**Check:** `GET /api/health-check` carries a `tracking` block with every ID, whether the secrets are set, and `sources` saying where each value came from.

## 5. Test run on staging

1. In Settings, Developers tab, switch **Tracking emit** on (`TRACKING_EMIT=true`). The staging site now renders the GTM snippet and sends conversions as live would.
2. Set `TRACKING_DEBUG=true` under Server-side tracking. GA4 events go to the validation endpoint instead of the live property and every delivery is logged. Set `FACEBOOK_TEST_EVENT_CODE` so Meta events land in the Test Events view.
3. Open GTM Preview mode (Tag Assistant) against the staging URL. The consent default appears first in the `dataLayer`, then `page_view`.
4. If the banner is on, decline, then accept. The theme calls `gtag('consent', 'update', ...)` and pushes `consent_analytics` and `consent_marketing`; the tags fire after the accept, not before.
5. Submit a form. Preview shows `form_submit` and `generate_lead` from the browser; the log shows the GA4 validation response and, in Meta's Test Events view, the server copy with the same event ID.
6. Switch **Tracking emit** off again and clear `TRACKING_DEBUG` and the test event code. The tracking health check warns as long as a staging site is left emitting.

**Check:** `curl -s https://staging.client.ch/ | grep -c gtm.js` is `0` again after step 6.

## 6. Publish and verify on live

Publish the container. On the live site, the environment type lets tracking emit on its own; `TRACKING_EMIT` stays unset.

```sh
curl -s https://client.ch/ | grep -c gtm.js    # 1
curl -s https://client.ch/api/health-check | jq .tracking
```

* GA4 DebugView shows `generate_lead` once from the browser and once from the server with the same event ID when both paths are on.
* `/evolve/health` shows the tracking check green. It fails on live when GTM or GA4 is missing or malformed or `TRACKING_EMIT=false` silences the site.
* Fleet reads the `tracking` block of `/api/health-check` and compares it across environments.

## 7. Keep it working

* Leads carry the visitor's first landing page, referrer and UTM parameters from the `footprints` cookie, independent of the tags, so attribution in the panel keeps working when a visitor declines.
* For a site that deliberately has no tracking, set `HEALTH_CHECK_TRACKING=false` instead of living with a failing check.
* The ID of a new platform goes into the container's variable and, where Evolve uses it server-side (GA4, Meta), into Settings > Tracking as well.

## Done when

* The consent default on every page matches the policy the client chose, and the banner, where on, updates it.
* Live renders the GTM snippet, staging does not.
* A form submission on live arrives in GA4 from the browser and the server with the same event ID.
* The tracking health check is green on live and silent on staging.
