Skip to main content

The touch block

The touch is how a person arrived: the campaign parameters, the referrer, the landing page, the ad click ids, and where that was captured. It travels inside features as one object, touch, declared once in the catalogue and identical on every activity that carries revenue, so that revenue per campaign needs no join at query time.

The block goes inside features, beside the activity's own fields; it is not a top-level field of the activity. A whole request, with the block in place and the rest shortened to placeholders (…); here a cashless payment that a campaign brought in:

{
"batch": [
{
"activity_id": "0192f1a4-7c3e-7b21-9d0a-5e3f8c2a1b45",
"activity": "made_cashless_payment",
"ts": "2026-10-17T20:00:00+02:00",
"identifiers": [{ "type": "email", "value": "ana@example.com", "role": "buyer" }],
"revenue": { "amount": "12.50", "currency": "EUR" },
"features": {
"transaction_id": "…",
"event_id": "…",
"point_of_sale": "…",
"touch": {
"captured_by": "shop",
"landing_url": "https://summerfest.nl/?utm_source=newsletter&utm_medium=email&utm_campaign=early-bird",
"referrer": "https://mail.example.com/",
"utm": { "source": "newsletter", "medium": "email", "campaign": "early-bird", "term": null, "content": null },
"click_ids": { "gclid": "Cj0KCQjw…" }
}
}
}
]
}
FieldMeaning
captured_byWhich system captured the touch: shop, site, …
landing_urlThe first page of the session, as the visitor arrived on it, query included. Kept whole.
referrerWhere the visitor came from, as the browser reported it.
utmsource, medium, campaign, term, content, each as typed in the address or null.
click_idsAd click ids by platform key (gclid, fbclid, ttclid, …), as the configuration lists them.

Every field is optional and the block itself may be null; a sender that knows nothing about the campaign leaves it out.

Where it comes from​

  • On the web, the browser SDK captures the touch on the first call of a session and the service puts it on started_session. Click ids are read only with ads consent.
  • On orders, the ticketing system's shop records the touch with the order, and the mapper copies it onto completed_order and onto every line that carries revenue (issued_ticket, issued_product), so a ticket's revenue is attributed without joining the order.
  • Elsewhere, a sender that knows the touch (a cashless payment during an event that was attributed to a campaign) may put it on made_cashless_payment or refunded_ticket in the same shape.

Rules​

  • The shape is fixed: the latest version of every revenue-bearing activity declares the same block, and the catalogue refuses to load otherwise. A new field in the touch is a new version of all of them.
  • UTMs are stored as typed, not normalised to "paid social" or "email": that mapping is a read-time view, so it can change without reloading anything.
  • The touch says nothing about who the person is: it carries no identifier, and the click ids are not identifiers.

Which activities carry it today: see the activity pages; the field table marks touch where it is declared.