Skip to content
Data
Skill

/walkeros-using-transformer-ga4

`@walkeros/transformer-ga4` decodes Google Analytics 4 Measurement Protocol v2 hits (`/g/collect`, `/mp/collect`) into walkerOS events. It sits in a server source's `before` chain, reads the raw HTTP request via `ctx.ingest`, and returns one walkerOS event per GA4 event in the

BOOST
From plugin
walkeros
34822 skills
Install
$ npx -y skills add elbwalker/walkerOS --skill walkeros-using-transformer-ga4 --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/walkeros-using-transformer-ga4

Context preview

The summary Claude sees to decide when to auto-load this skill.

`@walkeros/transformer-ga4` decodes Google Analytics 4 Measurement Protocol v2 hits (`/g/collect`, `/mp/collect`) into walkerOS events. It sits in a server source's `before` chain, reads the raw HTTP request via `ctx.ingest`, and returns one walkerOS event per GA4 event in the

SKILL.md

walkeros-using-transformer-ga4.SKILL.md
name: walkeros-using-transformer-ga4
description:
  Use when wiring `@walkeros/transformer-ga4` into a server flow, overriding
  default GA4 event mappings, dropping events, adding custom event keys, or
  troubleshooting GA4 Measurement Protocol decoding. Covers the `before`-chain
  wiring contract, configuration recipes, and per-field patching with
  extend/remove.

Using `@walkeros/transformer-ga4`

Overview

`@walkeros/transformer-ga4` decodes Google Analytics 4 Measurement Protocol v2 hits (`/g/collect`, `/mp/collect`) into walkerOS events. It sits in a server source's `before` chain, reads the raw HTTP request via `ctx.ingest`, and returns one walkerOS event per GA4 event in the hit (one hit can carry many events).

Boundaries: server-side decoding in front of a server source, GA4 v2 only, `G-` tids only by default. Per-field patching via `extend`/`remove` is supported.

When to use this skill

  • You are wiring `transformer-ga4` into a server flow for the first time
  • You want to override one of the 33 default GA4 mappings
  • You need to drop a GA4 event (auto-fired noise like `user_engagement`)
  • You want to add a custom event key for a `gtag('event', '<name>', ...)` call
  • You need to widen `tidPattern` to accept Ads (`AW-`) or DC (`DC-`) traffic
  • Events are not arriving and you suspect the decoder is silently dropping them

Quickstart wiring

**Pick the source by runtime before copying the example below.** gtag.js can send several events in one POST body (one per line, as `text/plain`), so the source must keep a non-JSON body as `ingest.body`. These server sources do:

  • `@walkeros/server-source-express` (shown below), the source `runneros` and the

`walkeros/flow` image serve. A `text/plain` body that is valid JSON is parsed; any other `text/plain` body reaches the decoder as the raw string.

  • `@walkeros/server-source-fetch`: a `(Request) => Response` handler with no

`port`, for a runtime that calls it (Cloudflare Workers, Deno, Bun, Node.js 18+ with a fetch adapter), e.g. with `collector` from `startFlow`:

  import { Source } from '@walkeros/core';
  import type { SourceFetch } from '@walkeros/server-source-fetch';

  export default {
    fetch: Source.getSource<SourceFetch.Types>(collector, 'http').push,
  };

`runneros` and the `walkeros/flow` image cannot serve it (they only mount a source's Node `httpHandler`).

  • `sourceCloudFunction` from `@walkeros/server-source-gcp` on Google Cloud

Functions.

  • `sourceLambda` from `@walkeros/server-source-aws` on AWS Lambda. It answers a

POST without a body with 400, so body-less single-event hits are lost there.

The other sources take the same `ingest` and `before` config as the express example. The fetch source also takes `paths`; none of them takes `port`.

{
  "version": 4,
  "flows": {
    "default": {
      "config": { "platform": "server" },
      "sources": {
        "http": {
          "package": "@walkeros/server-source-express",
          "config": {
            "settings": {
              "port": 8080,
              "paths": ["/g/collect"]
            },
            "ingest": {
              "map": {
                "url": { "key": "url" },
                "path": { "key": "path" },
                "method": { "key": "method" },
                "body": { "key": "body" }
              }
            }
          },
          "before": "ga4"
        }
      },
      "transformers": {
        "ga4": { "package": "@walkeros/transformer-ga4" }
      },
      "destinations": {
        "log": { "package": "@walkeros/destination-demo" }
      }
    }
  }
}

**Wiring rules:**

  • The express source listens on `/collect` by default and matches paths exactly.

gtag.js sends to `/g/collect`, so set `settings.paths` to `["/g/collect"]` (or the full path, if the collector URL carries a path prefix). Without it every GA4 hit gets a 404.

  • On the website, point gtag at the collector origin:

`gtag('config', 'G-XXXXXXXXXX', { server_container_url: 'https://collect.example.com' })`. gtag.js appends `/g/collect`. With the walkerOS gtag destination, set `settings.ga4.server_container_url` (or `settings.ga4.transport_url`). Hits then go to the collector instead of Google. The `/g/collect` suffix, the body-less single-event POST, and one-line-per-event batches are gtag.js behaviour, not a walkerOS contract: confirm them in the browser Network tab.

  • `before: "ga4"` runs every request on the source through the decoder, and a

request without GA4 parameters is dropped. If the source also receives walkerOS events (for example on `/collect`), gate the chain: `"before": { "match": { "key": "ingest.path", "operator": "eq", "value": "/g/collect" }, "next": "ga4" }`.

  • Batched hits (several events in one POST body, one per line) reach the decoder

through `server-source-express` as the raw `text/plain` string, alongside GET and body-less POST hits. A body sent as `application/json` must be valid JSON, or express answers 400 before the decoder runs.

  • `transformer-ga4` belongs in `source.before`, not `destination.before`.

Decoding is a pre-collector concern: GA4 hits are not yet walkerOS events.

  • The source must populate `ctx.ingest.url` (required, string) and

`ctx.ingest.body` (optional, string). Raw text body only: pre-parsed JSON will not decode. The express, fetch, GCP and AWS server sources keep a non-JSON `text/plain` body (gtag's batched POST) as the raw string in `ingest.body`; only a body that parses as JSON arrives parsed.

  • One HTTP request can fan out to N walkerOS events.

**Common mistake:** `config.ingest` must be the `map` operator with direct field paths on the request scope (no `req.` prefix), e.g. `{ "map": { "url": { "key": "url" } } }`. The bare `{ "url": "req.url" }` form is silently inert: without a `map` operator no field is extracted, so `ctx.ingest` stays empty, the decoder reads no `url`, and the hit is dropped.

This wiring c

Read more
Ships withwalkeros

From browser to BigQuery and ad platforms - warehouse-native, and ready for AI agents via MCP.

Get the whole plugin

Other skills on walkeros.