Files
greeneclipse 8580c996c3 Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
2026-08-09 22:47:43 +02:00

8.3 KiB
Raw Permalink Blame History

Descrybe web — GTM container import

Importable Google Tag Manager workspace for apps/web analytics ($lib/analytics, Consent Mode v2, SPA page_view).

File Purpose
descrybe-web-container.json GTM export format version 2 — tags, triggers, variables

No real secrets. Placeholders only: G-XXXXXXXX, GTM-XXXXXXX.

Import fix notes

Invalid Parameter.type enum (STRING)

GTM import failed with:

File format is invalid. Error deserializing enum type [Type]. Unrecognized value [STRING].

Root cause: a previous edit set consentSettings.consentType list items to "type": "STRING", matching outdated API prose (“list item is of type STRING”). That value is not in the real Parameter type enum.

Official GTM API Parameter.type values (camelCase in the API; SNAKE_CASE in container import/export JSON):

API Import/export JSON
template TEMPLATE
integer INTEGER
boolean BOOLEAN
list LIST
map MAP
triggerReference TRIGGER_REFERENCE
tagReference TAG_REFERENCE

There is no string / STRING. Text values (including consent type names like analytics_storage) use TEMPLATE.

This file now uses:

"consentSettings": {
  "consentStatus": "NEEDED",
  "consentType": {
    "type": "LIST",
    "list": [{ "type": "TEMPLATE", "value": "analytics_storage" }]
  }
}

Invalid { / } in entity names

GTM rejects { / } in tag / trigger / variable names. An earlier draft named a tag GA4 Event - Custom Event ({{Event}}), which failed import with “The name contains invalid character: {.

That tag is now GA4 Event - Catch-all Custom Events. The GA4 event name parameter still uses {{Event}} (built-in Event variable) — macro syntax is valid in parameter values, not in entity names.

Prerequisites

  1. Create a GA4 property and note its Measurement ID (G-…).
  2. Create a GTM Web container (or use an existing empty workspace). Note the container public ID (GTM-…).
  3. App loads GTM only when PUBLIC_GTM_ID is a valid GTM-… id (see root .env.example and apps/web/src/lib/analytics.ts).

Import steps (re-import)

  1. Open Google Tag Manager → your Web container.
  2. AdminImport Container.
  3. Choose docs/gtm/descrybe-web-container.json.
  4. Choose a workspace (new or existing).
  5. Import option:
    • Prefer Merge into an empty/new workspace, or
    • Overwrite only if you intend to replace the workspace contents.
    • If you previously failed mid-import or have a partial copy, use a fresh workspace (or Overwrite) so you do not keep the old invalid-named tag.
  6. After import, open VariablesConstant - GA4 Measurement ID and set the value to your real Measurement ID (replace G-XXXXXXXX).
  7. Consent (verify): each GA4 tag imports with Additional Consent Checks requiring analytics_storage (consentStatus = NEEDED, list item type TEMPLATE). If Preview shows tags firing without analytics consent, open each GA4 tag → Advanced settingsConsent Settings → require analytics_storage. Add ad_storage / ad_user_data / ad_personalization only if you later add ads tags.
  8. Preview with Tag Assistant against a local/staging site that has analytics consent granted.
  9. SubmitPublish.
  10. Set PUBLIC_GTM_ID in the web env to the containers public ID (GTM-…) so it matches the published container. The placeholder GTM-XXXXXXX in the JSON is not a real container id — GTM assigns the public ID when you create the container; import merges into that container.

What the container includes

Variables

Name Type Notes
Constant - GA4 Measurement ID Constant Replace G-XXXXXXXX
DL - value / currency / items / transaction_id Data Layer Ecommerce fields (version 2)
DL - page_path / page_title / page_location Data Layer SPA page_view
DL - billing_term / plan / pack_id Data Layer Flat extras on checkout/purchase

Built-ins enabled: Event, Page URL, Page Path, Page Hostname, Referrer.

Triggers (Custom Event only)

Trigger Event name
CE - page_view page_view
CE - begin_checkout begin_checkout
CE - purchase purchase
CE - checkout_canceled checkout_canceled
CE - sign_up sign_up
CE - login login
CE - generate_lead generate_lead
CE - processing_job_started processing_job_started
CE - consent_update consent_update
CE - Other Custom Events (catch-all) Regex excluding the above + gtm.*

Do not add History Change or GA4 enhanced-measurement automatic page_view — the app already pushes page_view from trackPageview / AnalyticsHost.

Tags

Tag Fires on Behavior
GA4 Configuration Initialization (built-in 2147479573) sendPageView=false; consent: analytics_storage
GA4 Event - page_view CE - page_view Sends page_path, page_title, page_location
GA4 Event - begin_checkout CE - begin_checkout Maps currency, value, items, plus billing_term / plan / pack_id
GA4 Event - purchase CE - purchase Same ecommerce fields + transaction_id + flat extras
GA4 Event - Catch-all Custom Events Named core events + catch-all Event name parameter = {{Event}} (dataLayer event name)

Ecommerce mapping (begin_checkout / purchase)

Expected dataLayer shape (flat, same object as event):

{
  event: "begin_checkout", // or "purchase"
  currency: "USD",
  value: 99,
  items: [
    {
      item_id: "pro",
      item_name: "Pro",
      item_category: "subscription", // or "credit_pack"
      price: 99,
      quantity: 1
    }
  ],
  // purchase only:
  transaction_id: "cs_test_…",
  // optional flat extras:
  billing_term: "monthly",
  plan: "pro",
  pack_id: "pack_…"
}

Dedicated tags map those DL variables into GA4 event parameters. List / marketing prices may be approximates (e.g. plan card pricing before Stripe tax/discounts); use Stripe / server data for finance-grade reporting.

The app sets Consent Mode v2 defaults to denied, then updates via the CMP (updateConsentMode + consent_update dataLayer event). GTM tags are gated with Additional Consent Required → analytics_storage. Client-side trackEvent / trackPageview also no-op until analytics is granted.

After publish — env checklist

PUBLIC_GTM_ID=GTM-XXXXXXXX   # must match the published container public ID

Do not put the GA4 Measurement ID in the web app env for primary loading — measurement ID lives in the GTM variable; the app loads GTM only.

Re-import after this fix

If a previous import failed on STRING (or left a partial workspace):

  1. Use a new workspace (or Overwrite on an empty one) so you do not keep a half-imported state.
  2. AdminImport Container → choose docs/gtm/descrybe-web-container.json.
  3. Confirm all five GA4 tags show Consent → require analytics_storage.
  4. Set Constant - GA4 Measurement ID, Preview, then Publish.

Limitations / manual fix-ups

  • Placeholder account/container IDs (0000000000 / GTM-XXXXXXX) are rewritten by GTM on import into your container. If import rejects metadata, create an empty Web container first, then Merge this file.
  • Fingerprints are synthetic; GTM regenerates them after import.
  • Catch-all does not forward arbitrary event parameters to GA4 (only the event name). Dedicated ecommerce tags carry value / currency / items / transaction_id. Add event-parameter tables for other events if needed.
  • No ads tags in this export — no ad_* consent requirements yet.
  • items as a GA4 event parameter relies on GTM/GA4 accepting the DL array via the event settings table. If Preview shows empty items, switch the ecommerce tags to “Send ecommerce data” and push an ecommerce: { … } object from the app, or verify in Tag Assistant that {{DL - items}} resolves.
  • Re-export from GTM after your first successful import if you want a fingerprint-perfect baseline in git.